Поведение деления с плавающей запятой на ноль
считают
#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
будет +Inf,b
будет-Inf, и c
будет NaN. Но я также слышу слухи, что строго говоря поведение деления с плавающей запятой на ноль undefined и поэтому приведенный выше код не может считаться переносимым C++. (Это теоретически уничтожает целостность моего миллиона строк плюс стек кода. Ой.)
кто правильный?
примечание Я доволен реализации, но я говорю о кошачьей еде, демоническом чихании неопределено поведение здесь.
7 ответов
деление на ноль как целое число, так и с плавающей запятой являются неопределенным поведением [expr.mul]p4:
оператор binary / дает фактор, а оператор binary % дает остаток от деления от первого выражения ко второму. если второй операнд / или % равен нулю, поведение не определено. ...
хотя реализация может дополнительно поддерживать Приложение F который хорошо определен семантика деления с плавающей запятой на ноль.
мы можем видеть из этого отчета об ошибке clang дезинфицирующее средство clang рассматривает деление IEC 60559 с плавающей запятой на ноль как неопределенное это даже при том, что макрос _ _ STDC_IEC_559__ определяется, определяется системными заголовками и, по крайней мере, для clang не поддерживает Приложение F и поэтому для clang остается неопределенным поведение:
приложение F к стандарту C (МЭК 60559 / поддержка IEEE 754) определяет деление с плавающей запятой на ноль, но clang (3.3 и 3.4 снимок Debian) считает его неопределенным. Это неверно:
поддержка приложения F является факультативной, и мы ее не поддерживаем.
#if STDC_IEC_559
этот макрос определяется вашими системными заголовками, а не нами; это ошибка в заголовках вашей системы. (FWIW, GCC не полностью поддерживает приложение F либо, IIRC, так что это даже не ошибка, специфичная для Clang.)
этот отчет об ошибке и два других отчета об ошибке утилиты: делением с плавающей запятой на ноль не определено и clang должен поддерживать приложение F ISO C (IEC 60559 / IEEE 754) укажите, что gcc соответствует Приложение F относительно деления с плавающей запятой на ноль.
хотя я согласен, что это не до библиотеки C, чтобы определить STDC_IEC_559 безусловно, проблема специфична для clang. GCC не полностью поддерживает приложение F, но, по крайней мере, его цель-поддерживать его по умолчанию, и разделение четко определено с ним, если режим округления не изменен. в настоящее время не поддерживает IEEE 754 (по крайней мере, основные функции, такие как обработка деления на ноль), рассматривается как плохое поведение.
это дальнейшая поддержка со стороны gcc семантика математики с плавающей запятой в GCC wiki что означает, что - fno-сигнализация-nans является значением по умолчанию, которое согласуется с документация по параметрам оптимизации gcc Он говорит:
значение по умолчанию-fno-signaling-nans.
интересно отметить, что утилиты для clang по умолчанию включает float-divide-by-zero под - fsanitize=undefined пока gcc не:
обнаружение деления с плавающей запятой на ноль. В отличие от других подобных вариантов, - fsanitize=float-divide-by-zero не включен-fsanitize=undefined, так как деление с плавающей запятой на ноль может быть законным способом получения бесконечностей и NaNs.
видеть жить для clang и жить для gcc.
стандарт C++ не заставляет стандарт IEEE 754, потому что это зависит в основном от аппаратной архитектуры.
Если аппаратное обеспечение / компилятор правильно реализуют стандарт IEEE 754, подразделение предоставит ожидаемые INF, - INF и NaN, в противном случае... это зависит от.
неопределенные средства, реализация компилятора решает, и есть много переменных, таких как аппаратная архитектура, эффективность генерации кода, лень разработчика компилятора, так далее..
источник:
в стандарт C++, что деление на 0.0 составляет undefined
Стандарт C++ 5.6.4
... Если второй операнд / или % равен нулю, поведение не определено
Стандарт C++ 18.3.2.4
...статический constexpr bool is_iec559;
...56. True только в том случае, если тип соответствует стандарту IEC 559.217
...57. Значимый для всех типы с плавающей запятой.
C++ обнаружение IEEE754:
стандартная библиотека включает шаблон для определения, поддерживается ли IEEE754:
статический constexpr bool is_iec559;
#include <numeric>
bool isFloatIeee754 = std::numeric_limits<float>::is_iec559();
что если IEEE754 не поддерживается?
это зависит, обычно деление на 0 вызывает аппаратное исключение и завершает работу приложения.
цитирую cppreference:
если второй операнд равен нулю, поведение не определено, за исключением того, что если происходит деление с плавающей запятой и тип поддерживает арифметику с плавающей запятой IEEE (см.
std::numeric_limits::is_iec559
), то:
если один операнд NaN, результат NaN
деления ненулевого числа на ±0.0 дает правильно-бесконечность и
FE_DIVBYZERO
is поднялиделение 0.0 на 0.0 дает NaN и
FE_INVALID
подняли
мы говорим о делении с плавающей запятой здесь, поэтому на самом деле реализация-определяется ли double
деление на ноль неопределено.
если std::numeric_limits<double>::is_iec559
is true
, а это "обычно true
", тогда поведение четко определено и дает ожидаемые результаты.
довольно безопасная ставка была бы плюх вниз:
static_assert(std::numeric_limits<double>::is_iec559, "Please use IEEE754, you weirdo");
... рядом с вашим кодом.
деление на 0 -неопределенное поведение.
из раздела 5.6 стандарт C++ (C++11):
бинарные
/
оператор дает фактор, а двоичный%
оператор возвращает остаток от деления первого выражения второй. если второй операнд/
или%
равно нулю поведение не определено. для целочисленных операндов/
оператор дает алгебраический фактор с любой дробной частью отбрасывается; если факторa/b
is представимо в виде результата,(a/b)*b + a%b
равнаa
.
нет различия между целочисленными и операндами с плавающей запятой для /
оператора. Стандарт только утверждает, что деление на ноль не определено без учета операндов.
в [expr] / 4 у нас есть
Если во время вычисления выражения, результат не определен математически или не в диапазоне представимых значений для ее типа поведение не определено. [ Примечание: большинство существующих реализаций C++ игнорируют целочисленные переполнения. Обработка деления на ноль, формирование остатка с использованием делителя нуля, и все исключения с плавающей запятой различаются между машинами и обычно регулируются библиотекой функция. - конец Примечания ]
выделено мной
таким образом, по стандарту это неопределенное поведение. Далее говорится, что некоторые из этих случаев фактически обрабатываются реализацией и настраиваются. Поэтому он не будет говорить, что он определен реализацией, но он дает вам знать, что реализации определяют некоторые из этого поведения.
Что касается вопроса отправителя: "кто прав?', это совершенно нормально говорить, что и ответы верны. Тот факт, что стандарт C описывает поведение как "неопределенное", не диктует, что на самом деле делает базовое оборудование; это просто означает, что Если вы хотите, чтобы ваша программа была значимой в соответствии со стандартом вы -не можете предположить, - что оборудование фактически реализует эту операцию. Но если вы работаете на оборудовании, которое реализуя стандарт IEEE, вы обнаружите, что операция фактически реализована, с результатами, предусмотренными стандартом IEEE.
Это также зависит от среды с плавающей запятой.
cppreference и детали: http://en.cppreference.com/w/cpp/numeric/fenv (хотя примеров нет).
Это должно быть доступно в большинстве сред рабочего стола/сервера C++11 и C99. Существуют также специфические для платформы вариации, которые предшествовали стандартизации всего этого.
Я ожидал бы, что включение исключений делает код более медленным, поэтому, вероятно, по этой причине большинство платформы, которые я знаю об исключениях disable по умолчанию.