I need to take 2 unsigned 8-bit values and subtract them, then add this value to a 32-bit accumulator. The 8-bit subtraction may underflow, and that's ok (unsigned int underflow is defined behavior, so no problems there).
I would expect that static_cast<uint32_t>(foo - bar)
should do what I want (where foo
and bar
are both uint8_t
). But it would appear that this casts them first and then performs a 32-bit subtraction, whereas I need it to underflow as an 8-bit variable. I know I could just mod 256, but I'm trying to figure out why it works this way.
Example here: https://ideone.com/TwOmTO
uint8_t foo = 5;
uint8_t bar = 250;
uint8_t diff8bit = foo - bar;
uint32_t diff1 = static_cast<uint32_t>(diff8bit);
uint32_t diff2 = static_cast<uint32_t>(foo) - static_cast<uint32_t>(bar);
uint32_t diff3 = static_cast<uint32_t>(foo - bar);
printf("diff1 = %u\n", diff1);
printf("diff2 = %u\n", diff2);
printf("diff3 = %u\n", diff3);
Output:
diff1 = 11
diff2 = 4294967051
diff3 = 4294967051
I would suspect diff3
would have the same behavior as diff1
, but it's actually the same as diff2
.
So why does this happen? As far as I can tell the compiler should be subtracting the two 8-bit values and then casting to 32-bit, but that's clearly not the case. Is this something to do with the specification of how static_cast
behaves on an expression?
The static_cast operator can be used for operations such as converting a pointer to a base class to a pointer to a derived class.
static_cast is the first cast you should attempt to use. It does things like implicit conversions between types (such as int to float , or pointer to void* ), and it can also call explicit conversion functions (or implicit ones).
As we learnt in the generic types example, static_cast<> will fail if you try to cast an object to another unrelated class, while reinterpret_cast<> will always succeed by "cheating" the compiler to believe that the object is really that unrelated class.
Static casting is done by the compiler: it treats the result as the target type, no matter what. You do this when you're absolutely sure about the argument being of the target type. Dynamic casting is done at runtime, and thus requires runtime type information.
For most of the arithmetic operators (including -
), the operands undergo the usual arithmetic conversions. One of these conversions is that any value of type narrower than int
is promoted to int
. (Standard reference: [expr]/10
).
So the expression foo - bar
becomes (int)foo - (int)bar
giving (int)-245
. Then you cast that to uint32_t
which will give a large positive number.
To get the result you are intending , cast to uint8_t
instead of uint32_t
. Alternatively, use the modulus operator %
on the result of the cast to uint32_t
.
It is not possible to do a calculation directly in narrower precision than int
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With