Es bien sabido que los NaN se propagan en aritmética, pero no pude encontrar ninguna demostración, así que escribí una pequeña prueba:
#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;
}
El ejemplo ( correr en vivo aquí ) produce básicamente lo que esperaría (lo negativo es un poco extraño, pero tiene sentido):
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan
MSVC 2015 produce algo similar. Sin embargo, Intel C ++ 15 produce:
neg: -nan(ind)
sub: nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan
Específicamente, qNaN - qNaN == 0.0
.
Esto ... no puede estar bien, ¿verdad? ¿Qué dicen los estándares relevantes (ISO C, ISO C ++, IEEE 754) sobre esto, y por qué hay una diferencia de comportamiento entre los compiladores?
Nan-NaN
esNaN
. Perl y Scala también se comportan de manera similar.-ffast-math
en gcc)?Respuestas:
El manejo predeterminado de coma flotante en el compilador Intel C ++ es
/fp:fast
, que manejaNaN
de forma insegura (lo que también resultaNaN == NaN
ser,true
por ejemplo). Intente especificar/fp:strict
o/fp:precise
y vea si eso ayuda.fuente
/fp:fast
: si desea algo seguro , probablemente debería evitar mejor que aparezcan NaN en primer lugar y, en general, no usar==
con números de punto flotante. Confiar en la extraña semántica que IEEE754 asigna a NaN es pedir problemas.NaN==NaN
regresarfalse
?Esta . . . no puede estar bien, ¿verdad? Mi pregunta: ¿qué dicen los estándares relevantes (ISO C, ISO C ++, IEEE 754) sobre esto?
Petr Abdulin ya respondió por qué el compilador da una
0.0
respuesta.Esto es lo que dice IEEE-754: 2008:
Entonces, el único resultado válido para la resta de dos operandos de NaN silenciosos es un NaN silencioso; Cualquier otro resultado no es válido.
El estándar C dice:
(donde aquí NaN denota un NaN silencioso según F.2.1p1 "Esta especificación no define el comportamiento de la señalización de NaN. Generalmente usa el término NaN para denotar NaN silencioso")
fuente
Como veo una respuesta que impugna el cumplimiento de los estándares del compilador de Intel, y nadie más ha mencionado esto, señalaré que tanto GCC como Clang tienen un modo en el que hacen algo bastante similar. Su comportamiento predeterminado es compatible con IEEE:
- pero si pides velocidad a expensas de la corrección, obtienes lo que pides -
Creo que es completamente justo criticar la elección de incumplimiento de ICC , pero no leería toda la guerra de Unix en esa decisión.
fuente
-ffast-math
,gcc
ya no cumple con ISO 9899: 2011 con respecto a la aritmética de coma flotante.