【发布时间】:2018-09-07 09:07:22
【问题描述】:
我有一个遗留代码库,我们正在尝试将其从 devtoolset-4 迁移到 devtoolset-7。我注意到一个关于有符号整数溢出的有趣行为(具体来说是int64_t)。
有一个代码 sn-p 用于在乘以一大组整数时检测整数溢出:
// a and b are int64_t
int64_t product = a * b;
if (b != 0 && product / b != a) {
// Overflow
}
这段代码在 devtoolset-4 上运行良好。但是,使用 devtoolset-7,永远不会检测到溢出。
例如:当a = 83802282034166 和b = 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.2 和 g++ (GCC) 7.2.1。
【问题讨论】:
-
signed整数溢出在 C++ 中未定义。事后您无法可靠地检测到它,因为您将隐含地处于未定义的行为中.. -
一个正确的测试检查之前相乘,以避免未定义的行为。类似
if (std::numeric_limits<std::int64_t>::max() / b < a) { /* error */ }。 -
product == a * b,所以product / b != a可以优化为 ->a * b / b != a->a * 1 != a->a != a->false。 -
OP 声明他知道有符号整数溢出是 UB。链接到答案状态为“这是 UB”的问题没有用。
-
如果您有兴趣了解编译器对给定代码段的输出,您可以使用 godbolt.org。例如,我有tried your snippet,似乎条件已被 gcc 7.3 与 -O2 完全优化。要解释为什么未定义的行为会发生作用,您需要准确指定您正在使用的编译器、正在使用的版本、正在使用的标志,答案仍可能取决于其他因素。
标签: c++ integer-overflow devtoolset