Недавно я столкнулся с проблемой на 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
является 32b, long long
является 64b, и они оба следуют IEEE правила):
- Для линии № 2, скобка сначала вычисляется, используя
int
S в качестве промежуточных результатов , что приводит к переполнению и хранению -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 правильно дает мне все, что он дает (так как это не определено)?
двоичный оператор * имеет лево-правовую ассоциативность – PeterT
работает аналогично в java.не бросается, пока не дойдет до длинного, слева направо :) – eiran
Обратите внимание, что переполнение - это неопределенное поведение. Вероятно, вы просто собираетесь обернуть, но это необязательно, и компилятор может делать всевозможные странные вещи, особенно с такими константами, как вы: http://stackoverflow.com/questions/3948479/integer-overflow-and-undefined -behavior http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html –