Search code examples
c++unsigned-integer

Why is the output of fixed width unsigned integer negative while unsigned integer output wraps around as expected?


#include <iostream>

#define TRY_INT

void testRun() 
{
    #ifdef TRY_INT          //test with unsigned
    unsigned int value1{1}; //define some unsigned variables
    unsigned int value2{1};
    unsigned int value3{2};
    #else                   //test with fixed width
    uint16_t value1{1};     //define fixed width unsigned variables
    uint16_t value2{1};
    uint16_t value3{2};

    #endif

    if ( value1 > value2 - value3 )
    {
        std::cout << value1 << " is bigger than: " << value2 - value3 << "\n";
    }
    else
    {
        std::cout << value1 << " is smaller than: " << value2 - value3 << "\n";
    }

}

int main()
{
    testRun();

    return 0;
}

with unsigned integers I get:

1 is smaller than: 4294967295

with fixed width unsigned int, output is:

1 is smaller than: -1

My expectation was it would wrap around as well, does this have something to do with std::cout?


Solution

  • I guess it is caused by integral promotion. Citing form cppreference:

    ...arithmetic operators do not accept types smaller than int as arguments, and integral promotions are automatically applied after lvalue-to-rvalue conversion, if applicable.

    unsigned char, char8_t (since C++20) or unsigned short can be converted to int if it can hold its entire value range...

    Consequently, if uint16_t is just an alias for unsigned short on your implementation, value2 - value3 is calculated with int type and the result is also int, that's why -1 is shown.

    With unsigned int, no promotion is applied and the whole calculation is performed in this type.


    In the latest online C++ Draft, see [conv.prom/1]:

    A prvalue of an integer type other than bool, char16_­t, char32_­t, or wchar_­t whose integer conversion rank is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.