Целочисленное переполнение и порядок операций

Недавно я столкнулся с проблемой, связанной с моим кодом на 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;
}

Для меня порядок, в котором умножения будут происходить в любой из этих трех строк, не определен. Тем не менее, вот что я думал, что произойдет (при условии, int это 32б, long long 64b, и они оба следуют правилам IEEE):

  • Для строки № 2 скобки вычисляются первыми, используя 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

Решение

GCC правильно.

  1. Ассоциативность для умножения слева направо. Это означает, что все эти выражения оцениваются слева направо.
  2. Повышение до более высокого типа происходит только между двумя операндами разных типов. не замужем бинарный оператор.

Например, первое выражение анализируется как i * j * k * n * l = ((((i * j) * k) * n) * l) и продвижение происходит только тогда, когда вычисляется последнее из умножений, но в этот момент левый операнд уже неверен.

7

Другие решения

Стандарт однозначно определяет группировку следующим образом:

5.6. Мультипликативные операторы [expr.mul]

1 Мультипликативные операторы *, / и% group слева направо.

Это означает, что a * b * c оценивается как (a * b) * c, Соответствующий компилятор не может свободно оценивать его как a * (b * c),

3

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector