问题
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?
回答1:
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
回答2:
The issue is not the static_cast but the subtraction, the operands of additive operators have the usual arithmetic conversions applied to them and in this case the integral promotions which results in both operands of the subtraction being promoted to int:
static_cast<uint32_t>(foo - bar);
^^^ ^^^
On the other hand:
static_cast<uint8_t>(foo - bar);
would produce desired result.
from the draft C++ standard section 5.7
[expr.add] says:
The additive operators + and - group left-to-right. The usual arithmetic conversions are performed for operands of arithmetic or enumeration type.
this results in the integral promotions, section 5
[expr] says:
Otherwise, the integral promotions (4.5) shall be performed on both operands
which results in both operands being converted to int, section 4.5
[conv.prom] says:
A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) 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.
and then the static_cast to uint32_t is applied which results in a conversion which is defined as follows in section 4.7
[conv.integral]:
If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [
The questions Why must a short be converted to an int before arithmetic operations in C and C++? explains why types smaller than int are promoted for arithmetic operations.
来源:https://stackoverflow.com/questions/27931157/why-is-static-cast-on-an-expression-acting-distributively