Как работает целочисленное деление С++ для предельных и отрицательных значений?

Я сталкиваюсь с некоторыми странными результатами с целым делением в С++. Я пытаюсь вычислить это: -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, прерывание арифметических исключений.)