首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的签名整数值溢出?

C++中的签名整数值溢出?
EN

Stack Overflow用户
提问于 2018-03-28 15:33:03
回答 7查看 2.4K关注 0票数 5

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

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

代码语言:javascript
复制
// 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 != a转换为product != a * b,并达到相同的超出值(或者可能只是跳过基于上述语句的计算,其中product = a * b)?

我知道有符号整数溢出在C++中是一种“未定义的行为”,因此编译器的行为可能会在不同的实现中发生变化。但有人能帮我理解一下上面的行为吗?

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

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2018-03-28 15:43:11

由于签名溢出/下溢被归类为未定义的行为,编译器可以欺骗并假设它不会发生(这是在一两年前的一次Cppcon谈话中出现的,但我忘记了头上的对话)。因为您正在执行算法,然后检查结果,所以优化器可以对部分检查进行优化。

这是未测试的代码,但您可能需要如下所示:

代码语言:javascript
复制
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可能为负值,则此检查将无效。

根据哥德波特,您可以看到您的版本已经完全优化了检查。

票数 4
EN

Stack Overflow用户

发布于 2018-03-28 15:45:33

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

这意味着优化器可以假设从未发生过这种情况。a*b/ba,句号。

现代编译器进行静态的单任务优化。

代码语言:javascript
复制
// a and b are int64_t
int64_t product = a * b;
if (b != 0 && product / b != a) {
  // Overflow
}

变成:

代码语言:javascript
复制
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
}

评估结果为

代码语言:javascript
复制
if (__Y__ && false) {
  // Overflow
}

显然,正如__Z__aa!=afalse

代码语言:javascript
复制
int128_t big_product = a * b; 

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

SSA允许编译器实现像(a+1)>a这样的东西总是正确的,这可以简化许多循环和优化情况。这个事实依赖于一个事实,即有符号值溢出是不可侵犯的行为。

票数 6
EN

Stack Overflow用户

发布于 2018-03-28 15:54:04

有了product == a * b的知识,编译器/优化器就可以采取以下优化步骤:

代码语言:javascript
复制
b != 0 && product / b != a
b != 0 && a * b / b != a
b != 0 && a * 1 != a
b != 0 && a != a
b != 0 && false
false

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

我知道有符号整数溢出在C++中是一种“未定义的行为”,因此编译器的行为可能会在不同的实现中发生变化。但有人能帮我理解一下上面的行为吗?

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49538595

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档