С/C++ задачка: что будет, если разделить INT_MIN на -1

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

Предлагается такой вот код:

#include <limits.h>
 
int foo(int a, int b) {
  return a / b;
}
 
int main(void) {
  return foo(INT_MIN, -1);
}

Каков результат деления?

За подробным ответом — сюда.
  • -2
  • 30 января 2013, 14:56
  • rouming

Комментарии (14)

RSS свернуть / развернуть
Попробуйте заменить операцию деления на умножение — результат тоже занятный получится :)
0
а разве не тоже самое должно произойти? Вроде происходит простой выход за пределы.
0
Очевидно, вообще-то.
Модуль крайнего отрицательного числа на единицу больше, чем крайнего положительного.
Вот и возникает переполнение в рантайме.
А при оптимизации вычисление происходит на этапе компиляции, и подставляется уже готовое значение (хоть и неправильное), и ничего особенного не происходит. Зато в реальном проекте можно будет пожелать удачной отладки :)
0
очевидно-то оно очевидно… как деление на 0, но, к сожалению, баги сами себя не пофиксят. кулхацкерам на заметку:
креш PostgreSQL или антивируса
креш Windows 8

а можно придумать такой код:
int foo1(int a) {
    return a / -1;
}

который, казалось бы, делает тоже самое, если 'a == INT_MIN', только вот компилятор сгенерирует такой код:
0000000000000010 <foo1>:
  10:	89 f8                	mov    %edi,%eax
  12:	f7 d8                	neg    %eax
  14:	c3                   	retq   

где инструкция neg — Two's Complement Negation (эквивалент return 0 — a) и никаких исключений процессора генерироваться не будет, а получим мы опять INT_MIN.

т.е. да, поведение неопределено, все согласно стандарту С.
0
Теоретически, ISO-совместимый компилятор, может выдать один из двух результатов, во время деления двух целочисленных переменных со знаком (signed integer type): положительный или отрицательный.
-5 / 3 = -1; /* остаток -2 */
-5 / 3 = -2; /* остаток +1. Округление результата с положительным остатком */
0
Именно за это я ненавижу высокоуровневые языки программирования и прочие фреймворки. Только ассемблер — только хардкор ))).
0
  • avatar
  • f1n
  • 31 января 2013, 01:26
Весь цимес в том, что именно процессор x86 генерирует исключение, как при делении на ноль. А стандарт С лишь отмалчивается, не делая никаких проверок и не уточняя результат на других процессорах, пущай программист сам думает.
+1
По стандарту С все нормально, int хранит симметричный диапазон от -32767 до 32767. Впрочем, подобные грабли — визитная карточка С. Он заточен под написание эффективного кода, а не безошибочного.
Меня другое удивило — почему исключение floating point?
0
> По стандарту С все нормально
а что говорит стандарт? я лично этого не понял. есть две функции, которые, казалось бы, должны давать один и тот же результат при одинаковых параметрах, но результат разный:
int foo2(int a, int b) {
  return a / b;
}
int foo1(int a) {
    return a / -1;
}


> int хранит симметричный диапазон от -32767 до 32767
диапазон-то как раз не симметричный: -32768 до 32767

> Впрочем, подобные грабли — визитная карточка С
ради интереса написал то же самое на java и на free pascal. результат одинаков:
foo1: -2147483648
foo2: -2147483648

т.е. делается софтверная проверка и процессорное исключение не допускается, просто переполняется переменная.

> Меня другое удивило — почему исключение floating point?
я не знаю как на винде, на unix всего один сигнал, который отвечает за все математически/арифметические исключения -SIGFPE, вот он и генерируется системой. а для того, чтобы все-таки различить исключение, нужно позвать fegetexceptflag и почекать нужный бит. а оболочка bash этого не делает, а просто говорит: был сигнал floating point exception, убил вашу программу.
0
> int хранит симметричный диапазон от -32767 до 32767
диапазон-то как раз не симметричный: -32768 до 32767
В железе. Но не в стандарте. Впрочем, стандарт в этом плане традиционно обтекаем — int хранит не менее чем -32767..32767. Больше — пожалуйста. В том числе и несимметрично.
0
Скорее, «в том числе и симметрично», потому что несимметричность заложена на физическом уровне из-за того, что ноль входит в множество положительных чисел.
0
Насколько я помню стандарт, он описывает наименьший гарантированный диапазон для 16-битных чисел. Дополнительный код — отнюдь не единственный.
0
Впрочем, стандарт в этом плане традиционно обтекаем — int хранит не менее чем -32767..32767.
Это где вы такой стандарт читали? В стандарте только отношения чар/инт/лонг описаны. Конкретные границы для инта не описаны в стандарте. Конкретные видно только в описании для 1/2/4/8 байтовых величин, и они несеммитричные. Для инта границы выглядят как не меньше инт_мин и не больше инт_макс.
0
В стандарте довольно сложно найти требуемое, но, например, это говорится в завалявшемся у меня стандарте C99:
5.2.4.2.1 Sizes of integer types <limits.h>
The values givenbelowshall be replaced by constant expressions suitable for use in #if
preprocessing directives.… Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.

—minimum value for an object of type int INT_MIN -32767 // −(2^15 − 1)
—maximum value for an object of type int INT_MAX +32767 // 2^15 − 1
Для остальных знаковых целочисленных типов тоже указаны симметричные диапазоны.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.