¿Por qué NaN-NaN== 0.0 con el compilador Intel C++?
floating-point ieee-754 (3)
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?
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:
$ g++ -O2 test.cc && ./a.out
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan
$ clang++ -O2 test.cc && ./a.out
neg: -nan
sub: -nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan
- pero si pides velocidad a expensas de la corrección, obtienes lo que pides -
$ g++ -O2 -ffast-math test.cc && ./a.out
neg: -nan
sub: nan nan 0.000000
add: nan nan
div: nan nan 1.000000
mul: nan nan
$ clang++ -O2 -ffast-math test.cc && ./a.out
neg: -nan
sub: -nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan
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.
El manejo predeterminado de coma flotante en el compilador Intel C ++ es
/fp:fast
, que maneja los
NaN
de forma insegura (lo que también hace que
NaN == NaN
sea
true
por ejemplo).
Intente especificar
/fp:strict
o
/fp:precise
y vea si eso ayuda.
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 respuesta de
0.0
.
Esto es lo que dice IEEE-754: 2008:
(6.2 Operaciones con NaNs) "[...] para una operación con entradas de NaN silenciosas, distintas de las operaciones máximas y mínimas, si se va a entregar un resultado de coma flotante, el resultado será un NaN silencioso que debería ser uno de los ingrese NaNs ".
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:
(C11, F.9.2 Transformaciones de expresión p1) "[...]
x - x → 0. 0 "Las expresiones x - x y 0. 0 no son equivalentes si x es un NaN o infinito"
(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")