b\n"); } I was expecting that b would get promoted to int and the compiler would complain ab" /> b\n"); } I was expecting that b would get promoted to int and the compiler would complain ab" /> b\n"); } I was expecting that b would get promoted to int and the compiler would complain ab"/>

Does uint16_t get promoted to int? And is it safe to typecast uint16_t to uint32_t?

987 Views Asked by At
uint32_t a = 10;
uint16_t b = 0;

if (a > b)
{
   printf("a > b\n");
}

I was expecting that b would get promoted to int and the compiler would complain about comparing signed and unsigned. But the compiler did not raise any warning.

When I change if (a > b) to if (a > (b * 10)) the compiler says

warning: comparison between signed and unsigned

Why is this so?

And is it safe to typecast this to uint32_t?

if (a > (uint32_t)(b * 10))
3

There are 3 best solutions below

2
Lundin On BEST ANSWER

Does uint16_t gets promoted to int?

If int is 32 bits, sure. If not, then it isn't promoted.

I was expecting that b would get promoted to int

It is, on a 32 bit system.

But the compiler did not raise any warning.

It's not really the compilers job to keep track of that. If you get a warning, it's just a bonus. But also please note that in your example the conversion is done in two internal steps: first the integer promotions is performed on the uint16_t making it int. But then immediately afterwards, the usual arithmetic conversions convert that int to a uint32_t. So essentially as far as the programmer is concerned, the uint16_t is converted to uint32_t and that conversion is always safe.

Generally, operations mixing unsigned operands of different sizes are safe.

if (a > (uint32_t)(b * 10))

b gets promoted to int and 10 is already int. There's no way for the multiplication to overflow since it can't get larger than 655350. You can cast to uint32_t afterwards if you like, but it doesn't rally achieve anything except being explicit.

More rugged code would have done the cast before the multiplication or perhaps used 10u instead. But in this specific case it really don't matter.

1
AndyG On

Yes, integer promotion* happens.

You can verify it using the std::same_as type trait to query the type of your expression:

static_assert(std::is_same_v<int, 
   decltype(std::declval<uint16_t>() * std::declval<int>())>); // b*10

Instead of casting the expression (b*10) to uint32_t, cast 10 to uint32_t:

b * uint32_t(10)

static_assert(std::is_same_v<uint32_t,
   decltype(std::declval<uint16_t>() * uint32_t(std::declval<int>()))>); // b * (uint32_t)(10)

* if int can represent all the values of the source type, otherwise, promotion to unsigned int might happen.

0
chux - Reinstate Monica On

Does uint16_t gets promoted to int?

A uint16_t often goes through the usual promotions. It will convert to an int if there is no change in value (e.g. int is wider than 16-bit), otherwise it converts to an unsigned. In either case, there is no change in value and no warning is needed.


And is it safe to typecast uint16_t to uint32_t?

Yes.

Note that (uint32_t)(b * 10) is not certainly a uint16_t to uint32_t conversion.

(b * 10) happens first.

16-bit int/unsigned: Both b, 10 convert to unsigned and then the multiplication occurs, possibly truncating the product if it was mathematically more than 0xFFFF. Then the unsigned is converted to uint32_t.

32-bit int/unsigned: b is converted to an int. Multiplication occurs and there is no overflow given the range of uint16_t and 10. Then the int product is converted to uint32_t/unsigned with no value change.


When I change if (a > b) to if (a > (b * 10)) the compiler says ...

A good alternative is to perform the b * 10 using at least the type of a. This will perform the unsigned multiplication to at least the width of uint32_t.

// if (a > (b * 10))
if (a > b * UINT32_C(10))