我需要取2个无符号8位值并减去它们,然后将该值加到32位累加器中。 8位减法可能会下溢,而且没问题(unsigned int underflow是定义的行为,因此没有问题)。
我希望static_cast<uint32_t>(foo - bar)
能够做我想做的事情(foo
和bar
都是uint8_t
)。但看起来这会先抛出它们然后然后执行32位减法,而我需要它作为8位变量下溢。我知道我可以只修改256,但我试图找出为什么以这种方式工作。
此处示例: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);
输出:
diff1 = 11
diff2 = 4294967051
diff3 = 4294967051
我怀疑diff3
与diff1
具有相同的行为,但它实际上与diff2
相同。
那么为什么会这样呢?据我所知,编译器应该减去两个8位值然后转换为32位,但事实并非如此。这是否与static_cast
在表达式上的行为规范有关?
答案 0 :(得分:8)
对于大多数算术运算符(包括-
),操作数经历通常的算术转换。其中一种转换是,任何比int
更窄的类型值都会提升为int
。 (标准参考:[expr]/10
)。
所以表达式foo - bar
变为(int)foo - (int)bar
给出(int)-245
。然后你将其转换为uint32_t
,这将给出一个大的正数。
要获得您想要的结果,请转到uint8_t
而不是uint32_t
。或者,对转换结果使用模数运算符%
uint32_t
。
不可能以比int
答案 1 :(得分:4)
问题不是static_cast而是减法,加法运算符的操作数具有应用于它们的通常算术转换,在这种情况下是整数促销,这导致减法的两个操作数被提升为 INT :
static_cast<uint32_t>(foo - bar);
^^^ ^^^
另一方面:
static_cast<uint8_t>(foo - bar);
会产生预期的结果。
草案C ++标准部分5.7
[expr.add] 说:
加法运算符+和 - 从左到右分组。执行通常的算术转换 算术或枚举类型的操作数。
这导致整体促销,5
[expr] 部分说:
否则,应在两个操作数上执行整体促销(4.5)
导致两个操作数都转换为 int ,部分4.5
[conv.prom] 表示:
除bool,char16_t,char32_t或wchar_t之外的整数类型的整数转换的整数转换 如果int可以表示all,则rank(4.13)小于int的rank可以转换为int类型的prvalue 源类型的值;否则,源prvalue可以转换为unsigned类型的prvalue 中间体
然后应用static_cast到 uint32_t ,这导致转换在4.7
[conv.integral] 部分中定义如下:
如果目标类型是无符号的,则结果值是与源一致的最小无符号整数 整数(模2n,其中n是用于表示无符号类型的位数)。 [
问题Why must a short be converted to an int before arithmetic operations in C and C++?解释了为什么小于 int 的类型被提升用于算术运算。