整数溢出和运算顺序

Integer overflow and order of operations

我最近遇到了一个关于我的 C++ 代码的问题,这让我想知道我是否对编译器对长操作的处理有一些误解... 看看下面的代码:

#include <iostream>

int main() {
    int i = 1024, j = 1024, k = 1024, n = 3;
    long long l = 5;
    std::cout << i * j * k * n * l << std::endl;  // #1
    std::cout << ( i * j * k * n ) * l << std::endl; // #2
    std::cout << l * i * j * k * n << std::endl;  // #3
    return 0;
}

对我来说,这 3 行中任何一行的乘法运算顺序是不确定的。然而,这是我认为会发生的事情(假设 int 是 32b,long long 是 64b 并且它们都遵循 IEEE 规则):

现在,对于第 1 行和第 3 行,我不确定:我认为虽然计算顺序未定义,但编译器会将所有操作 "promote" 设为最大操作数的类型,即这里的long long。因此,在这种情况下不会发生溢出,因为所有计算都将在 64b 中进行...但这就是 GCC 5.3.0 为这段代码提供的内容:

~/tmp$ g++-5 cast.cc
~/tmp$ ./a.out 
-5368709120
-5368709120
16106127360

我本以为第一个结果也是 16106127360。由于我怀疑 GCC 中是否存在如此严重的编译器错误,我猜错误就在键盘和椅子之间。

任何人都可以确认/确认这是未定义的行为,GCC 给我的任何东西都是正确的(因为这是未定义的)?

海湾合作委员会是正确的。

  1. 乘法结合律是left to right。这意味着所有这些表达式都是从左到右求值的。
  2. 仅在 单个 二元运算符的不同类型的两个操作数之间升级到更高类型。

例如,第一个表达式被解析为 i * j * k * n * l = ((((i * j) * k) * n) * l) 并且仅在计算最后一个乘法时才进行提升,但此时左操作数已经不正确。

标准明确定义分组如下:

5.6 Multiplicative operators [expr.mul]

1 The multiplicative operators *, /, and % group left-to-right.

这意味着 a * b * c 被评估为 (a * b) * c。符合标准的编译器没有自由将其评估为 a * (b * c)