4

私は最近、私の 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 long64bで、両方ともIEEEルールに従っていると仮定します):

  • 2 行目では、中間結果として s を使用してかっこが最初に評価さintれ、オーバーフローが発生して -1073741824 が格納されます。この中間結果は最後の乗算に昇格されるlong longため、出力される結果は -5368709120 になります。
  • 評価の順序が定義されていないため、1 行目と 3 行目は「同等」です。

ここで、行 #1 と #3 については不明です。評価の順序は定義されていませんが、コンパイラはすべての操作を最大のオペランドの型、つまりlong longここに「昇格」させると思いました。したがって、この場合、すべての計算が 64b で行われるため、オーバーフローは発生しません...しかし、これは GCC 5.3.0 がこのコードに対して私に与えるものです:

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

最初の結果も 16106127360 と予想していました。GCC にこの規模のコンパイラのバグがあるとは思えないので、バグはキーボードと椅子の間にあると思います。

これは未定義の動作であり、GCCは私に与えるものは何でも正しいことを確認/確認してください(これは未定義であるため)?

4

2 に答える 2

7

GCCは正しいです。

  1. 乗算の結合規則は左から右です。つまり、これらの式はすべて左から右に評価されます。
  2. 上位の型への昇格は、異なる型の 2 項演算子の 2 つのオペランド間でのみ行われます

たとえば、最初の式は次のように解析されi * j * k * n * l = ((((i * j) * k) * n) * l)、昇格は最後の乗算が計算されたときにのみ発生しますが、この時点で左側のオペランドは既に正しくありません。

于 2016-01-15T13:12:54.970 に答える
3

規格では、グループ化を次のように明確に定義しています。

5.6 乗法演算子 [expr.mul]

1 乗法演算子 *、/、および % は、左から右にグループ化されます。

これは、a * b * cが として評価されることを意味し(a * b) * cます。準拠するコンパイラには、それを として評価する自由はありませんa * (b * c)

于 2016-01-15T13:13:44.530 に答える