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

Viewed 69
#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?

2 Answers

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.

unsigned int is equivalent to uint32_t and unsigned short int is equivalent to uint16_t.

Therefore, if you use unsigned short int instead of unsigned int you will get the same behavior as for uint16_t.

Why do you get -1?

Integral promotion will try to convert unsigned short int to int if int can hold all possible values of unsigned short int. On the other hand, if that is not the case, integral promotion to unsigned int will be performed.

Therefore the subtraction is most likely done in the type int, not uint16_t.

Related