Поведение деления с плавающей точкой на ноль

Рассматривать

#include <iostream>
int main()
{
double a = 1.0 / 0;
double b = -1.0 / 0;
double c = 0.0 / 0;
std::cout << a << b << c; // to stop compilers from optimising out the code.
}

Я всегда думал, что a будет + Инф, b будет -Inf, и c будет NaN. Но я также слышу слухи о том, что, строго говоря, поведение деления с плавающей точкой на ноль не определено и, следовательно, приведенный выше код не может считаться переносимым C ++. (Это теоретически уничтожает целостность моего миллиона строк плюс стек кода. Упс.)

Кто прав?

Обратите внимание, я доволен реализация определена, но я говорю о кошачьей еде, чихании демонов неопределенное поведение Вот.

42

Решение

Деление на ноль, как целое число, так и число с плавающей точкой — неопределенное поведение [Expr.mul] р4:

Двоичный / оператор дает частное, а бинарный оператор% — остаток от деления
первого выражения вторым. Если второй операнд / или% равен нулю, поведение не определено.

Хотя реализация может дополнительно поддерживать Приложение F которая имеет четко определенную семантику для деления с плавающей точкой на ноль.

Мы можем видеть из этого сообщения об ошибке Clang Clang Sanitizer рассматривает деление с плавающей точкой по стандарту IEC 60559 на ноль как неопределенное что хотя макрос __STDC_IEC_559__ определяется, определяется системными заголовками и, по крайней мере, для clang не поддерживает Приложение F и так для clang остается неопределенное поведение:

Приложение F к стандарту C (поддержка IEC 60559 / IEEE 754) определяет
деление с плавающей точкой на ноль, но лязг (3.3 и 3.4 снимок Debian)
считает это неопределенным. Это неверно:

Поддержка Приложения F не является обязательной, и мы не поддерживаем ее.

#если STDC_IEC_559

Этот макрос определяется вашими системными заголовками, а не нами; это
ошибка в заголовках вашей системы. (FWIW, GCC не полностью поддерживает Приложение
F, IIRC, так что это даже не ошибка Clang.)

Это сообщение об ошибке и два других сообщения об ошибках UBSan: деление с плавающей точкой на ноль не является неопределенным а также Clang должен поддерживать Приложение F ISO C (IEC 60559 / IEEE 754) указать, что gcc соответствует Приложение F по отношению к плавающей запятой делим на ноль.

Хотя я согласен с тем, что не до библиотеки C определять STDC_IEC_559 безусловно, проблема специфична для лязга. GCC не полностью поддерживает Приложение F, но по крайней мере его намерение состоит в том, чтобы поддерживать его по умолчанию, и разделение четко определено с ним, если режим округления не изменен. В настоящее время не поддержка IEEE 754 (по крайней мере, базовые функции, такие как обработка деления на ноль) считается плохим поведением.

Это дальнейшая поддержка со стороны GCC Семантика математики с плавающей точкой в ​​GCC Wiki что указывает на то, что —FNo пренебрежимо малых сигнализации это значение по умолчанию, которое согласуется с Документация по опциям оптимизации gcc который говорит:

По умолчанию используется -fno-signaling-nans.

Интересно отметить, что UBSan для лязг по умолчанию в том числе поплавкового деления на ноль под -fsanitize = не определено в то время как GCC не:

Обнаружение деления с плавающей точкой на ноль. В отличие от других подобных опций, -fsanitize = float-делить на ноль не включается параметром -fsanitize = undefined, поскольку деление с плавающей точкой на ноль может быть законным способом получения бесконечностей и NaN.

Видеть это жить для лязга а также жить для GCC.

5

Другие решения

Стандарт C ++ не обязывает стандарт IEEE 754, потому что это зависит в основном от аппаратной архитектуры.

Если аппаратное обеспечение / компилятор правильно реализуют стандарт IEEE 754, подразделение предоставит ожидаемые INF, -INF и NaN, в противном случае … это зависит.

Неопределенное означает, что реализация компилятора решает, и есть много переменных к этому как архитектура аппаратного обеспечения, эффективность генерации кода, лень разработчика компилятора, и т.д ..

Источник:

Стандарт C ++ утверждает, что деление на 0,0 undefined

Стандарт C ++ 5.6.4

… Если второй операнд / или% равен нулю, поведение не определено

Стандарт С ++ 18.3.2.4

…статический constexpr bool is_iec559;

…56. Истинно, если и только если тип соответствует стандарту IEC 559.217

…57. Имеет значение для всех типов с плавающей точкой.

C ++ обнаружение IEEE754:

Стандартная библиотека включает шаблон для определения, поддерживается ли IEEE754 или нет:

статический constexpr bool is_iec559;

#include <numeric>
bool isFloatIeee754 = std::numeric_limits<float>::is_iec559();

Что если IEEE754 не поддерживается?

Это зависит, обычно, деление на 0 вызывает аппаратное исключение и заставляет приложение завершаться.

38

квотирование cppreference:

Если второй операнд равен нулю, поведение не определено, за исключением того, что если происходит деление с плавающей запятой и тип поддерживает арифметику с плавающей запятой IEEE (см. std::numeric_limits::is_iec559), затем:

  • если один операнд равен NaN, результат равен NaN

  • деление ненулевого числа на ± 0.0 дает правильно подписанную бесконечность и FE_DIVBYZERO Поднялся

  • деление 0,0 на 0,0 дает NaN и FE_INVALID Поднялся

Мы говорим здесь о делении с плавающей точкой, так что на самом деле это определяется реализацией double деление на ноль не определено.

Если std::numeric_limits<double>::is_iec559 является true, и это «обычно true«, тогда поведение четко определено и дает ожидаемые результаты.

Довольно безопасным вариантом было бы добавить:

static_assert(std::numeric_limits<double>::is_iec559, "Please use IEEE754, you weirdo");

… рядом с вашим кодом.

23

Деление на 0 неопределенное поведение.

Из раздела 5.6 Стандарт C ++ (C ++ 11):

Бинарный / оператор дает частное, а двоичный % оператор
дает остаток от деления первого выражения на
второй. Если второй операнд / или же % ноль, поведение
не определено.
Для интегральных операндов / оператор дает алгебраический
частное с любой дробной частью отбрасывается; если частное a/b является
представимый в типе результата, (a/b)*b + a%b равно a ,

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

7

В [expr] / 4 мы имеем

Если во время оценки выражения, результат не определен математически или не в диапазоне представимых значений для его типа, поведение не определено. [Примечание: большинство существующих реализаций C ++ игнорируют целочисленные переполнения. Обработка деления на ноль, формирования остатка с использованием делителя нуля и всех исключений с плавающей запятой варьируется в зависимости от машины и обычно настраивается библиотечной функцией. —Конечная записка]

Акцент мой

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

6

Что касается вопроса автора «Кто прав?», То вполне нормально сказать, что и то и другое ответы верны. Тот факт, что стандарт C описывает поведение как «неопределенное», НЕ диктует, что на самом деле делает базовое оборудование; это просто означает, что если вы хотите, чтобы ваша программа имела смысл в соответствии со стандартом Вы не можете предположить, что аппаратное обеспечение фактически выполняет эту операцию. Но если вы работаете на оборудовании, которое реализует стандарт IEEE, вы обнаружите, что операция фактически выполнена с результатами, предусмотренными стандартом IEEE.

1

Это также зависит от среды с плавающей запятой.

У cppreference есть детали:
http://en.cppreference.com/w/cpp/numeric/fenv
(нет примеров, хотя).

Это должно быть доступно в большинстве сред C ++ 11 и C99 для настольных компьютеров и серверов. Существуют также специфичные для платформы варианты, которые предшествуют стандартизации всего этого.

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

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector