C ++中签名的Integer值溢出?

时间:2018-03-28 15:33:03

标签: c++ integer-overflow devtoolset

我有一个遗留代码库,我们正试图从devtoolset-4迁移到devtoolset-7。我注意到有关有符号整数溢出的有趣行为(int64_t,具体而言)。

有一个代码片段,用于检测整数溢出,同时乘以一大组整数:

// a and b are int64_t
int64_t product = a * b; 
if (b != 0 && product / b != a) {
    // Overflow
}

此代码与devtoolset-4一起正常运行。但是,使用devtoolset-7时,永远不会检测到溢出。

例如:当a = 83802282034166b = 98765432时, product变为-5819501405344925872(显然值已溢出)。

product / b导致的价值等于a (83802282034166)。因此if条件永远不会成立。 其值应根据溢出(负)product值计算:-5819501405344925872 / 98765432 = -58922451788

具有讽刺意味的是,数学是正确的,但它会导致与devtoolset-4相关的异常行为。

  • 编译器是否可以缓存该值(而不是重新评估它)导致此行为?
  • 或编译器优化是否会转换语句product / b != aproduct != a * b并达到相同的溢出值(或者可能只是根据product = a * b上面的语句跳过计算?

我知道有符号整数溢出是一种未定义的行为'在C ++中,编译器行为可能会在实现中发生变化。但有人可以帮助我理解上述行为吗?

注意:devtoolset-4和devtoolset-7中的g ++版本分别为g++ (GCC) 5.2g++ (GCC) 7.2.1

7 个答案:

答案 0 :(得分:6)

有符号整数溢出是C ++中未定义的行为。

这意味着优化器可以假设它永远不会发生。 a*b/ba,期间。

现代编译器进行基于静态单一赋值的优化。

// a and b are int64_t
int64_t product = a * b;
if (b != 0 && product / b != a) {
  // Overflow
}

变为:

const int64_t __X__ = a * b; 
const bool __Y__ = b != 0;
const int64_t __Z__ = __X__ / b;
const int64_t __Z__ = a*b / b;
const int64_t __Z__ = a;

if (__Y__ && __Z__ != a) {
  // Overflow
}

评估为

if (__Y__ && false) {
  // Overflow
}

显然,__Z__aa!=afalse

int128_t big_product = a * b; 

使用big_product并检测那里的溢出。

SSA允许编译器实现(a+1)>a始终为真的事情,这可以简化许多循环和优化案例。这个事实依赖于签名值的溢出是不可取的行为。

答案 1 :(得分:4)

因为签名的溢出/下溢被归类为未定义的行为,编译器被允许作弊并假设它不会发生(这是在一两年前的Cppcon谈话中出现的,但我忘了我的谈话头)。因为您正在进行算术运算然后检查结果,优化器会优化部分检查。

这是 未经测试的 代码,但您可能需要以下内容:

if(b != 0) {
    auto max_a = std::numeric_limits<int64_t>::max() / b;
    if(max_a < a) {
        throw std::runtime_error{"overflow"};
    }
}
return a * b;

请注意,此代码不处理下溢;如果a * b可能是否定的,则此检查无效。

根据Godbolt,您可以看到您的版本已完全优化了检查。

答案 2 :(得分:4)

知道product == a * b,编译器/优化器可以采取以下优化步骤:

b != 0 && product / b != a
b != 0 && a * b / b != a
b != 0 && a * 1 != a
b != 0 && a != a
b != 0 && false
false

优化器可以选择完全删除分支。

  

我知道有符号整数溢出是一种未定义的行为&#39;在C ++中,编译器行为可能会在实现中发生变化。但有人可以帮助我理解上述行为吗?

你可能知道有符号整数溢出是UB,但我想你还没有掌握UB的真正含义。 UB并不需要,而且往往没有意义。这个案子似乎很直接。

答案 3 :(得分:0)

  

有人可以帮助我理解上述行为吗?

有符号整数溢出在C ++中有未定义的行为。这意味着您无法可靠地检测到它,并且包含有符号整数溢出的代码可以执行任何操作

如果要检测操作是否会导致有符号整数溢出,则需要在溢出发生之前执行此操作,以防止UB发生。

答案 4 :(得分:0)

Signed integer overflow is undefined behavior。这与unsigned int(所有无符号整数)不同。 有关此here

的更多信息

作为旁注,人们注意到使用int代替unsigned int会提高性能(请参阅here),因为编译器不会处理溢出行为。

答案 5 :(得分:0)

如果你担心整数溢出,那么使用任意精度整数库并不是更好 - 用这个你可以将你的大小类型增加到128位而不用担心它。

https://gmplib.org/

答案 6 :(得分:0)

您可以阅读本文档,它可能对您有用,就像我在变量和数据类型中遇到的任何问题一样,我直接阅读它:http://www.cplusplus.com/doc/tutorial/variables/