Как работает целочисленное деление С++ для предельных и отрицательных значений?
Я сталкиваюсь с некоторыми странными результатами с целым делением в С++. Я пытаюсь вычислить это: -2147483648/-1.
Я получаю 3 разных результата в 3 разных сценариях:
int foo(int numerator, int denominator) {
int res = numerator / denominator; // produces SIGFPE, Arithmetic exception interrupt
cout << res << endl;
}
int main() {
int res = -2147483648 / -1;
cout << res << endl; // prints -2147483648
cout << -2147483648 / -1 << endl; // prints 2147483648
foo(-2147483648, -1);
return 0;
}
Почему операция целочисленного деления приводит к различным результатам в разных ситуациях?
Ответы
Ответ 1
Литерал -2147483648 / -1
вычисляется вашим компилятором как 2147483648
в типе данных, достаточно широком, чтобы удерживать это значение.
Когда литерал распечатывается напрямую, он правильно печатает значение.
Когда литерал хранится в res
, он преобразуется в int
. int
, по-видимому, имеет 32 бита в вашей системе. Значение 2147483648
не может быть представлено как 32-разрядное целое число со знаком, поэтому приведение приводит к переполнению. В вашей системе это переполнение приводит к значению -2147483648
(вероятно, он использует два дополнения).
Наконец, при попытке выполнить деление во время выполнения (в функции foo
) исключение SIGFPE
возникает из-за переполнения (поскольку тип данных int
не может представлять результат).
Обратите внимание, что все эти три параметра зависят от поведения, зависящего от платформы:
- тот факт, что компилятор не генерирует никаких ошибок (или других проблем) при переполнении литерального вычисления и просто использует тип данных, достаточно большой для хранения результата.
- тот факт, что переполнение
int
при хранении литерала генерирует это конкретное значение (и никаких других проблем)
- тот факт, что исключение
SIGFPE
возникает при переполнении во время выполнения
Ответ 2
Ваш результат может быть INT_MAX+1
, другими словами, он, вероятно, переполняется. Это Undefined Поведение, и все может случиться. Например, компилятор может полностью отказаться от кода.
(Система может иметь INT_MAX >= 2147483648
, но тогда вы ожидали бы тот же результат для трех тестовых камер)
Ответ 3
int res = -2147483648 / -1;
cout << res << endl; // prints -2147483648
cout << -2147483648 / -1 << endl; // prints 2147483648
int res = numerator / denominator; // produces SIGFPE, Arithmetic exception interrupt
Обратите внимание, что нет отрицательных целых литералов.
Нет отрицательных целых литералов. Выражения, такие как -1, применяют унарный оператор минус к значению, представленному литералом, что может включать неявные преобразования типов.
Литерал 2147483648
больше максимального значения int
, поэтому его тип будет long
(или long long
, зависит от реализации). Тогда тип -2147483648
имеет значение long
, а результат вычисления (-2147483648 / -1
) тоже long
.
В первом случае результат 2147483648
типа long
неявно преобразован в int
, но он больше максимального значения int
, результат определяется реализацией. (Кажется, результат обернут в соответствии с правилами представления (2 дополнения) здесь, поэтому вы получите результат -2147483648
.)
Для второго случая результат с типом long
распечатывается напрямую, поэтому вы получаете правильный результат.
В третьем случае вы выполняете вычисления на двух int
s, и результат не может соответствовать типу результата (т.е. int
), подписан произошло превышение целых арифметических операций, поведение undefined. (Производит здесь прерывание SIGFPE, прерывание арифметических исключений.)