Деление на ноль в C

Когда я компилирую программу:

#include <stdio.h>

int main(void)
{
    int x, y = 0;

    x = 1 / y;
    printf("x = %d\n", x);
    return 0;
}

Это дает "Исключение с плавающей точкой (ядро сброшено)"

Тем не менее, когда я компилирую:

#include <stdio.h>

int main(void)
{
    double x, y = 0;

    x = 1 / y;
    printf("x = %f\n", x);
    return 0;
}

Печатает "х = инф"

Почему он возвращает x как бесконечное значение, если вы используете double, но возвращает ошибку, если вы используете int?

3 ответа

Стандарт C явно заявляет, что деление на ноль имеет неопределенное поведение для целочисленных операндов или операндов с плавающей точкой.

C11 6.5.5 пункт 5:

Результат / оператор является частным от деления первого операнда на второй; результат % оператор является остатком. В обеих операциях, если значение второго операнда равно нулю, поведение не определено.

Неопределенное поведение означает, что стандарт ничего не говорит о том, что происходит. Это может привести к значимому или бессмысленному результату, может привести к краху или, как говорится в стандартной шутке, заставить демонов вылететь из носа. (Конечно, последнего не произойдет, но это не нарушит стандарт С, если это произойдет.)

Типы с плавающей точкой, в отличие от целочисленных типов, часто имеют специальные значения, которые не представляют числа. Стандарт IEEE с плавающей точкой определяет, что деление на ноль может привести к результату Infinity, что и делает ваша реализация. Не существует значения "Бесконечность" для целых чисел. (Обратите внимание, что реализации C могут или не могут соответствовать стандарту IEEE с плавающей запятой.)

В этом вопросе обсуждается семантика деления на ноль в IEEE с плавающей точкой.

Стандарт C определяет, что поведение деления на ноль для операндов арифметических типов не определено (см., Например, этот онлайн-проект стандарта C):

6.5.5 Мультипликативные операторы

2 Каждый из операндов должен иметь арифметический тип. Операнды оператора% должны иметь целочисленный тип.

5 Результатом оператора / является частное от деления первого операнда на второй; результат оператора% - остаток. В обеих операциях, если значение второго операнда равно нулю, поведение не определено.

И это правило явно включает значения с плавающей запятой, поскольку термин арифметические типы означает целые значения и значения с плавающей запятой:

6.2.5 Типы

18 Целочисленные и плавающие типы вместе называются арифметическими типами.

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

Переменная с плавающей точкой может фактически хранить значение, представляющее бесконечность. (Это значение INFINITY определено в math.h.) Но нет целочисленного представления бесконечности, поэтому единственное, что нужно сделать, это потерпеть неудачу.

Другие вопросы по тегам