奇怪的整数溢出逻辑

时间:2013-04-05 12:44:15

标签: c++ integer-arithmetic

对于下面的代码我得到溢出但遗憾的是我似乎无法理解为什么。

std::int8_t smallValue         = -1;
unsigned int value             = 500;
std::uint8_t anotherSmallValue = 1;

auto test = smallValue * value * anotherSmallValue;

之后test是一个非常大的值。

有人可以解释一下,这里会发生什么?

5 个答案:

答案 0 :(得分:6)

结果类型为unsigned int test heresmallValue * value smallValue将被转换为无符号,因此此表达式为(unsigned)-1 * 500。但是,如果使用-Wsign-conversion编译此代码 - 编译器告诉您,您做了坏事。 link

答案 1 :(得分:5)

当编译器看到smallValue * value时,它必须决定结果的数据类型是什么,给定输入数据类型signed(8位)和unsigned int(通常,16或32位)。 C ++的规则规定在这种情况下,结果将是无符号的。因此,smallValue * value的值不能像您期望的那样-500;相反,值-500被解释为正数。

此外,您在此处将8位值乘以通常为16位或32位的值。在这种情况下,C ++的规则规定较小的存储值将首先转换为与较大的存储值相同的大小;所以在这种情况下,smallValue * value的结果确实足够大,可以存储多个幅度500

继续乘以无符号数量anotherSmallValue(= 1)会导致另一个unsigned具有相同的值。

由于您使用的是auto,因此返回类型推断为unsigned

只需转回signed(例如,将值test定义为int,而不是auto,反过来通常会整个操作的结果返回到signed值,而不是在内部更改位;这将正如您所期望的那样正确显示-500;但是,正如其他海报所指出的那样,这是相当危险的理论,因为它在技术上并不能保证工作,尽管它通常会以今天的编译器的方式工作。

答案 2 :(得分:2)

只有前两个变量(smallValue * value)才会得到相同的结果。

首先,对这两个值应用积分促销:int8_t变为intunsigned int保持不变。

然后应用C ++ 11 5/9中的这个规则来确定结果类型:

  

否则,如果具有无符号整数类型的操作数的等级大于或等于   另一个操作数的类型的等级,有符号整数类型的操作数应转换为   具有无符号整数类型的操作数的类型

所以-1必须使用模运算转换为unsigned int,给出一个大的正数。乘以500将溢出(再次使用模运算),给出不同的大数。

答案 3 :(得分:1)

将'auto'变为签名类型,你会没事的:

long test = smallValue * value * anotherSmallValue;

答案 4 :(得分:1)

使用混合类型操作总是容易出现此类错误。我建议你使用单一类型进行算术运算