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

Sti*_*eta 4 c++ integer-overflow devtoolset

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

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

// a and b are int64_t
int64_t product = a * b; 
if (b != 0 && product / b != a) {
    // Overflow
}
Run Code Online (Sandbox Code Playgroud)

这段代码与devtoolset-4一起工作正常.但是,使用devtoolset-7时,永远不会检测到溢出.

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

product / b结果价值等于a (83802282034166).因此,if情况永远不会成真.它的值应该根据overflown(负)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.

Yak*_*ont 6

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

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

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

// a and b are int64_t
int64_t product = a * b;
if (b != 0 && product / b != a) {
  // Overflow
}
Run Code Online (Sandbox Code Playgroud)

变为:

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
}
Run Code Online (Sandbox Code Playgroud)

评估为

if (__Y__ && false) {
  // Overflow
}
Run Code Online (Sandbox Code Playgroud)

显然,由于__Z__aa!=afalse.

int128_t big_product = a * b; 
Run Code Online (Sandbox Code Playgroud)

big_product那里工作并检测溢出.

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


Ste*_*ell 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;
Run Code Online (Sandbox Code Playgroud)

请注意,此代码不处理下溢;如果a * b可能为负数,则此检查不起作用。

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

  • 并且负数也没有被处理(“-big * -big”和“big * -big”) (3认同)