2016-08-04 3 views
14

Я столкнулся с некоторыми странными результатами с целым делением на C++. Я пытаюсь вычислить это: -2147483648/-1.Как работает целочисленное деление C++ для предельных и отрицательных значений?

Что я получаю 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

Стоит упомянуть, что код не компилируется на Windows VS-2015, говоря «отрицательная интегральная константа, преобразованная в неподписанный тип \t» и «унарный минус», примененный к неподписанному типу, результат все равно unsigned' на всех строках '-2147483648/-1' –

+1

Простой ответ [здесь] (http://stackoverflow.com/a/29355979/1460794). – wally

+2

Вот как это делает визуальная студия: '#define INT_MIN (-2147483647 - 1) // минимальная (подписанная) int value' – wally

ответ

12

Буквальный -2147483648/-1 вычисляется компилятором, как 2147483648 в типе данных, который является достаточно широким, чтобы держать это значение ,

Когда литерал распечатывается напрямую, он правильно печатает значение.

Когда литерал хранится в res, он отливается int. В вашей системе есть int. Значение 2147483648 не может быть представлено как 32-битное целое число со знаком, поэтому приведение приводит к переполнению. В вашей системе это переполнение приводит к значению -2147483648 (скорее всего, он использует два дополнения).

Наконец, при попытке выполнить разделение во время выполнения (в функции foo), то SIGFPE исключение происходит из-за переполнения (потому что int тип данных не может представлять результат).

Обратите внимание, что все эти три варианта полагаться на платформу зависимого поведении:

  • тот факт, что компилятор не создает какие-либо ошибок (или другие проблемы), когда буквенное переполнение вычисления и просто использует тип данных достаточно большие для хранения результата
  • того факта, что int переполнения при хранении в буквальном смысле, что генерирует определенное значение (и никаких другие вопросов)
  • того факта, что SIGFPE исключения, когда переполнение во время выполнения
+0

Переполнение целочисленного числа AFAICT во время компиляции - Undefined Behavior; использование более крупного типа данных - одно возможное поведение, но, как показывает FirstSTEP, другое поведение - ошибка. – MSalters

+0

@MSalters: да, я указываю столько же в последнем абзаце. –

+0

Что также неправильно-иш: каждая платформа должна поддерживать достаточно широкий тип данных; 'long long int', безусловно, достаточно широк. – MSalters

12

Ваш результат может быть INT_MAX+1, другими словами, он, вероятно, переполняется. Это неопределенное поведение, и все может случиться. Например, компилятор может отклонить код напрямую.

(система может иметь INT_MAX >= 2147483648, но тогда можно было бы ожидать того же результата для ваших 3 testcases)

10
int res = -2147483648/-1; 
cout << res << endl;    // prints -2147483648 
cout << -2147483648/-1 << endl; // prints 2147483648 
int res = numerator/denominator; // produces SIGFPE, Arithmetic exception interrupt 

Примечание не вы никакого отрицательного integer literals.

Нет отрицательных целых литералов. Выражения, такие как -1, применяют унарный оператор минус к значению, представленному литералом, что может включать неявные преобразования типов.

Буквальная 2147483648 больше, чем максимальное значение из int, поэтому его типа будет long (или long long, зависит от реализации).Тогда -2147483648 имеет тип long, а результат расчета (-2147483648/-1) - long.

За 1-й случай, результат 2147483648 типа long является implicitly converted к int, но это больше, чем максимальное значение из int, результат реализации. (Кажется, результат обернут в соответствии с правилами представления (дополнение 2) здесь, так что вы получите результат -2147483648.)

Для второго случая распечатывается результат с типом long, поэтому вы получить правильный результат.

Для 3-й случай, вы делаете расчет на двух int с, и результат не может поместиться в тип результата (т.е. int), signed integer arithmetic operation overflow случилось, поведение не определено. (Здесь генерируется прерывание SIGFPE, прерывание арифметических исключений.)

+0

'-2147483648' может быть' long long', например. на MSVC (который имеет 'LONG_MAX = 2147483647') – MSalters

+0

iirc в старых версиях C++ он также может быть 32-разрядным беззнаковым долгом (который фактически не изменяет конечный результат, но он меняет то, как вы туда попадете). – plugwash

+0

@plugwash Я не знаю много о «старшем» C++. :) На данный момент это будет 'long' или' long long' (начиная с C++ 11), это не будет 'unsigned', если не использовать суффикс' u'. Представление определяется реализацией, поэтому 'long' может быть представлено 32-разрядным целым числом. – songyuanyao

Смежные вопросы