Деление на ноль в 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 = inf»

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


person Evan Waddicor    schedule 01.02.2019    source источник
comment
Целые числа не имеют inf или NaN.   -  person tkausl    schedule 02.02.2019
comment
Целочисленное деление вызывает исключение процессора, тогда как деление с плавающей запятой имеет представление.   -  person Weather Vane    schedule 02.02.2019
comment
Связанный с этим вопрос stackoverflow .com/questions/23878400/   -  person imreal    schedule 02.02.2019
comment
@WeatherVane - очень красивая новая иконка/аватар...   -  person David C. Rankin    schedule 02.02.2019
comment
@DavidC.Rankin, ты сам неплохо выглядишь!   -  person Weather Vane    schedule 02.02.2019
comment
Основная причина заключается в том, что 1 / 0 => Inf математически неверно, но числа с плавающей запятой являются неточными значениями, и может случиться так, что в ваших вычислениях делитель был настолько мал, что его округлили до 0, поэтому все же предпочтительнее произвести некоторые результат, чем просто сбой всей программы, чтобы вы знали, где она пошла не так. Именно это и делает IEEE 754 в большинстве случаев. Однако это неразумно делать с целыми числами, а целые числа не могут представлять бесконечность.   -  person Antti Haapala    schedule 02.02.2019
comment
Обратите внимание, что вы можете получить бесконечность уже с 1 / 1e-309, что на несколько порядков меньше, чем математическая бесконечность ;-)   -  person Antti Haapala    schedule 02.02.2019


Ответы (3)


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

С11 6.5.5 пункт 5:

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

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

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

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

person Keith Thompson    schedule 01.02.2019

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

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

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

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

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

6.2.5 Типы

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

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

person Stephan Lechner    schedule 02.02.2019

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

person Willis Blackburn    schedule 01.02.2019
comment
Это не единственное, что нужно сделать. Может произойти бесконечное количество вещей, например, демоны могут появиться из вашего носа. - person KamilCuk; 02.02.2019
comment
Вероятно, стоит отметить, что бесконечность не является математически правильным результатом деления числа на ноль — скорее, результат математически не определен. - person Jeremy Friesner; 02.02.2019
comment
@JeremyFriesner: Верно, но стандарт IEEE с плавающей запятой указывает бесконечность как результат деления на ноль (для некоторых настроек). Реализация C может соответствовать или не соответствовать IEEE. - person Keith Thompson; 02.02.2019
comment
@JeremyFriesner: Тогда разве 1.0/0.0 не должно быть NaN? Бесконечность вступает в игру, когда вы берете предел 1,0/*x*, поскольку x стремится к 0 слева (- ∞) или справа (+ ∞). - person jxh; 02.02.2019
comment
@jxh: stackoverflow.com/q/14682005/827263 обсуждает правила IEEE для деления на ноль. - person Keith Thompson; 02.02.2019
comment
Не в процессоре для новичков, который занял бы бесконечное количество времени, вычисляя его путем вычитания. - person Weather Vane; 02.02.2019
comment
@JeremyFriesner: Единого определения арифметики не существует. Арифметика возникает из аксиом, и разные системы арифметики служат разным целям. - person Eric Postpischil; 02.02.2019
comment
так что единственное, что можно сделать, это потерпеть неудачу.; на самом деле нет - поведение не определено, и нельзя полагаться на его сбой (что бы ни означал сбой). - person Stephan Lechner; 02.02.2019
comment
@StephanLechner на самом деле IEEE здесь более применим, чем стандарт C. - person 0___________; 02.02.2019
comment
@P__J__: Разработка - это не процесс угадывания применимых спецификаций. Они должны быть указаны явно, и идеально правильный дизайн с желаемым поведением логически выводится из спецификаций. Этот вопрос помечен тегом C, а не IEEE-754, что говорит нам: «Этот тег следует использовать с общими вопросами, касающимися языка C, как определено в стандарте ISO 9899 (последняя версия, 9899: 2018, если не указано иное — также тег запросы для конкретной версии с c89, c99, C11 и т. д.)». Таким образом, мы должны предпочесть стандарт C в качестве основы для ответа. - person Eric Postpischil; 02.02.2019
comment
Хорошо, результат деления целого числа на ноль в C технически не определен, но совершенно очевидно, что на машине OP это приводит к ошибке. ОП спросил, почему он генерирует ошибку, а не может ли он полагаться на такое поведение. - person Willis Blackburn; 02.02.2019
comment
@EricPostpischil не согласился. Все аппаратное и библиотечное программное обеспечение marh соответствует стандарту IEEE, а не стандарту. Си - person 0___________; 02.02.2019
comment
@P__J__: Нет, не знают. Даже если бы они это сделали, вопрос касается C, а не аппаратного обеспечения или библиотек. - person Eric Postpischil; 02.02.2019
comment
@EricPostpischil на этот вопрос невозможно ответить, не принимая во внимание реализацию. Тогда это UB, которые с практической точки зрения ничего не значат, когда вы выполняете некоторую числовую работу. - person 0___________; 02.02.2019
comment
@P__J__: Представление о том, что отсутствие ответа на вопрос оправдывает предположения, абсурдно. Было бы разумно заявить, что C не определяет поведение, но наблюдая, что полученный результат может быть или даже вероятным, потому что их реализация использует некоторые функции стандарта IEEE 754, который распространен. Обратите внимание, что использование некоторых функций IEEE 754 не равнозначно соответствию IEEE 754 и не предполагает применения IEEE 754. Это предоставление информации ОП, которая может привести их к дальнейшему обучению и исследованию без утверждения утверждений, о которых известно, что они неверны. - person Eric Postpischil; 02.02.2019