Хорошо известно, что NaN распространяются в арифметике, но я не смог найти никаких демонстраций, поэтому я написал небольшой тест:
#include <limits>
#include <cstdio>
int main(int argc, char* argv[]) {
float qNaN = std::numeric_limits<float>::quiet_NaN();
float neg = -qNaN;
float sub1 = 6.0f - qNaN;
float sub2 = qNaN - 6.0f;
float sub3 = qNaN - qNaN;
float add1 = 6.0f + qNaN;
float add2 = qNaN + qNaN;
float div1 = 6.0f / qNaN;
float div2 = qNaN / 6.0f;
float div3 = qNaN / qNaN;
float mul1 = 6.0f * qNaN;
float mul2 = qNaN * qNaN;
printf(
"neg: %f\nsub: %f %f %f\nadd: %f %f\ndiv: %f %f %f\nmul: %f %f\n",
neg, sub1,sub2,sub3, add1,add2, div1,div2,div3, mul1,mul2
);
return 0;
}
Пример ( запущенный здесь ) дает в основном то, что я ожидал (отрицание немного странно, но это имеет смысл):
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan
MSVC 2015 производит нечто подобное. Тем не менее, Intel C ++ 15 производит:
neg: -nan(ind)
sub: nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan
В частности, qNaN - qNaN == 0.0
.
Это ... не может быть правдой, верно? Что об этом говорят соответствующие стандарты (ISO C, ISO C ++, IEEE 754), и почему существует разница в поведении между компиляторами?
Nan-NaN
естьNaN
. Perl и Scala также ведут себя одинаково.-ffast-math
gcc)?Ответы:
Обработка с плавающей запятой по умолчанию в компиляторе Intel C ++ -
/fp:fast
этоNaN
небезопасная обработка (что также приводитNaN == NaN
,true
например, к тому). Попробуйте указать/fp:strict
или/fp:precise
и посмотрите, поможет ли это.источник
/fp:fast
: если вы хотите что-то безопасное , вам, вероятно, лучше избегать появления NaN, и вообще не использовать==
с числами с плавающей запятой. Полагаясь на странную семантику, которую IEEE754 назначает NaN, возникает проблема.NaN==NaN
возвращенииfalse
?Это . , , не может быть правым, верно? Мой вопрос: что об этом говорят соответствующие стандарты (ISO C, ISO C ++, IEEE 754)?
Петр Абдулин уже ответил, почему компилятор дает
0.0
ответ.Вот что говорит IEEE-754: 2008:
Таким образом, единственный действительный результат для вычитания двух тихих операндов NaN - это тихий NaN; любой другой результат недействителен.
Стандарт C говорит:
(где здесь NaN обозначает тихий NaN согласно F.2.1p1 «Эта спецификация не определяет поведение сигнальных NaNs. Обычно для обозначения тихих NaN используется термин NaN»)
источник
Поскольку я вижу ответ, оспаривающий соответствие стандартам компилятора Intel, и никто больше об этом не упомянул, я укажу, что и GCC, и Clang имеют режим, в котором они делают что-то очень похожее. Их поведение по умолчанию соответствует IEEE -
- но если вы просите скорости за счет правильности, вы получаете то, что просите -
Я думаю, что было бы совершенно справедливо критиковать выбор ICC по умолчанию , но я бы не стал читать все войны Unix обратно в это решение.
источник
-ffast-math
,gcc
больше не соответствует ISO 9899: 2011 в отношении арифметики с плавающей запятой.