Solo una nota, 1 oz de prevención es mejor que 1 lb de cura. En otras palabras, evitar que se ejecute 0.f / 0.f es mucho mejor que verificar retroactivamente los nan's en su código. nanEsto puede ser terriblemente destructivo para su programa, si se permite que prolifere, puede introducir errores difíciles de encontrar. Esto se debe a que nanes tóxico, (5 * nan= nan), nanno es igual a nada ( nan! = nan), nanNo es mayor que nada ( nan!> 0), nanno es menor que nada ( nan! <0).
bobobobo
1
@bobobobo: Esa es una característica que permite la verificación centralizada de errores. Al igual que las excepciones frente a los valores de retorno.
Ben Voigt
2
¿Por qué <cmath> no tiene isnan ()? Está en std ::
frankliuao
Respuestas:
351
Según el estándar IEEE, los valores de NaN tienen la extraña propiedad de que las comparaciones que los involucran son siempre falsas. Es decir, para un flotador f, f != fserá verdadero solo si f es NaN.
Tenga en cuenta que, como han señalado algunos comentarios a continuación, no todos los compiladores respetan esto al optimizar el código.
Para cualquier compilador que afirme utilizar el punto flotante IEEE, este truco debería funcionar. Pero no puedo garantizar que va a funcionar en la práctica. Consulte con su compilador, si tiene dudas.
Es mejor que el compilador no elimine esto si se ejecuta en modo IEEE. Consulte la documentación de su compilador, por supuesto ...
dmckee --- ex-gatito moderador
38
-1 solo funciona en teoría, no en la práctica: compiladores como g ++ (con -fastmath) lo arruinan. La única forma general, hasta c ++ 0x, es probar el patrón de bits.
Saludos y hth. - Alf
66
@Alf: la documentación de la -ffast-mathopción dice explícitamente que puede dar como resultado resultados incorrectos para programas que dependen de una implementación exacta si las reglas / especificaciones IEEE o ISO para funciones matemáticas. Sin esa opción habilitada, el uso x != xes una forma perfectamente válida y portátil de probar NaN.
Adam Rosenfield
77
@ Adam: la documentación declara abiertamente que no es conforme, sí. y sí, me he encontrado con ese argumento antes, discutiendo esto extensamente con Gabriel Dos Reis. se usa comúnmente para defender el diseño, en un argumento circular (no sé si pretendías asociarte a eso, pero vale la pena saberlo: es una guerra de llamas). su conclusión que x != xes válida sin esa opción no sigue lógicamente. Puede ser cierto para una versión particular de g ++, o no. de todos modos, generalmente no tiene forma de garantizar que no se utilizará la opción de matemáticas rápidas.
Saludos y hth. - Alf
77
@Alf: No, no estaba al tanto de su discusión con Gabriel Dos Reis. Steve Jessop hizo un gran punto en la otra pregunta sobre asumir la representación de IEEE. Si asume IEEE 754 y que el compilador está funcionando de manera conforme (es decir, sin la -ffast-mathopción), entonces x != xes una solución válida y portátil. Incluso puede probar -ffast-mathprobando la __FAST_MATH__macro y cambiar a una implementación diferente en ese caso (por ejemplo, usar uniones y twiddling de bits).
Adam Rosenfield
220
No hay ninguna isnan()función disponible en la Biblioteca estándar de C ++ actual. Fue introducido en C99 y definido como una macro, no una función. Los elementos de la biblioteca estándar definidos por C99 no forman parte del estándar actual C ++ ISO / IEC 14882: 1998 ni su actualización ISO / IEC 14882: 2003.
En 2005 se propuso el Informe técnico 1. El TR1 trae compatibilidad con C99 a C ++. A pesar de que nunca se ha adoptado oficialmente para convertirse en estándar C ++, muchas (las implementaciones de GCC 4.0+ o Visual C ++ 9.0+ C ++ proporcionan características TR1, todas ellas o solo algunas (Visual C ++ 9.0 no proporciona funciones matemáticas C99) .
Si TR1 está disponible, entonces cmathincluye elementos C99 como isnan(), isfinite()etc., pero se definen como funciones, no macros, generalmente en el std::tr1::espacio de nombres, aunque muchas implementaciones (es decir, GCC 4+ en Linux o en XCode en Mac OS X 10.5+) los inyectan directamente a std::, por lo que std::isnanestá bien definido.
Además, algunas implementaciones de C ++ todavía hacen que C99 isnan()macro esté disponible para C ++ (incluido a través de cmatho math.h), lo que puede causar más confusión y los desarrolladores pueden asumir que es un comportamiento estándar.
Una nota sobre Viusal C ++, como se mencionó anteriormente, no proporciona std::isnanninguno std::tr1::isnan, pero proporciona una función de extensión definida como la _isnan()que ha estado disponible desde Visual C ++ 6.0
En XCode, hay aún más diversión. Como se mencionó, GCC 4+ define std::isnan. Para versiones anteriores del compilador y la biblioteca de XCode, parece que (aquí hay una discusión relevante ), no he tenido la oportunidad de verificarme) se definen dos funciones, __inline_isnand()en Intel y __isnand()en Power PC.
Todos quieren estas funciones como isNan o isInfinity. ¿Por qué las personas a cargo no simplemente incluyen en sus estándares? - Trataré de averiguar cómo ponerme a cargo y votar por esto. Seriamente.
shuhalo
8
@shuhalo A cargo todavía?
Tomáš Zato - Restablece a Monica
11
Esta respuesta debe actualizarse ya std::isnanque ahora forma parte del estándar C ++ 11 y el soporte se ha extendido. std :: isnan fue implementado en Visual Studio comenzando con Visual Studio 2013. Tal vez conseguido @shuhalo a cargo :-)
aberaud
170
Primera solución: si está utilizando C ++ 11
Como se preguntó esto, hubo un poco de nuevos desarrollos: es importante saber que std::isnan()es parte de C ++ 11
Tenga en cuenta que esto es incompatible con -fast-math si usa g ++, consulte a continuación otras sugerencias.
Otras soluciones: si utiliza herramientas que no cumplen con C ++ 11
Para C99, en C, esto se implementa como una macro isnan(c)que devuelve un valor int. El tipo de xserá flotante, doble o doble largo.
Varios proveedores pueden o no incluir o no una función isnan().
La forma supuestamente portátil de verificar NaNes usar la propiedad IEEE 754 que NaNno es igual a sí misma, es decir x == x, será falsa por xser NaN.
Sin embargo, la última opción puede no funcionar con todos los compiladores y algunas configuraciones (particularmente las configuraciones de optimización), por lo que, en último recurso, siempre puede verificar el patrón de bits ...
Definitivamente merece ser la respuesta aceptada y merece más votos a favor. Gracias por el consejo
LBes
3
−1std::isnan sigue siendo una recomendación incorrecta a partir de febrero de 2017, ya que no funciona con la optimización de punto flotante de g ++.
Saludos y hth. - Alf
@ Cheersandhth.-Alf: ¿esta opción cumple con IEEE? La respuesta ha sido editada
BlueTrin
@BlueTrin: Ambos x != xy isnanestán obligados a trabajar para cumplir con IEEE 754. Con respecto a esto último, el estándar IEEE 754-2008 establece que "Las implementaciones proporcionarán las siguientes operaciones no computacionales para todos los formatos aritméticos compatibles" y "isNaN (x) es verdadero si y solo si x es un NaN". Para verificar la conformidad que requiere el estándar is754version1985()y is754version2008(), en su lugar, donde C ++ ofrece std::numeric_limits<Fp>::is_iec559()(IEC 559 es el mismo estándar). Desafortunadamente con la -ffast-mathoptimización, por ejemplo, g ++ afirma conformidad pero no es conforme.
Saludos y hth. - Alf
1
Advertencia: isnan (x) no funciona con la opción -ffinite-math-only en gcc y clang
A Fog
82
También hay una biblioteca de solo encabezado presente en Boost que tiene herramientas ordenadas para lidiar con los tipos de datos de punto flotante
se agregó en Boost 1.35 (acabo de descubrir que mi programa no se compila en la antigua distribución de Linux).
marcin
2
si compila con la opción --fast-math entonces esta función no funcionará como se esperaba.
Gaetano Mendola
43
Hay tres formas de "oficiales": POSIX isnanmacro , C ++ 0x isnanplantilla de función , o Visual C ++ _isnanfunción .
Desafortunadamente, es poco práctico detectar cuál de ellos usar.
Y desafortunadamente, no hay una manera confiable de detectar si tiene representación IEEE 754 con NaNs. La biblioteca estándar ofrece una forma oficial ( numeric_limits<double>::is_iec559). Pero en la práctica, los compiladores como g ++ lo arruinan.
En teoría, uno podría usar simplemente x != x, pero los compiladores como g ++ y visual c ++ lo arruinan.
Por lo tanto, al final, pruebe los patrones de bits NaN específicos , suponiendo (¡y con suerte haciendo cumplir, en algún momento!) Una representación particular como IEEE 754.
EDITAR : como un ejemplo de "compiladores como g ++ ... arruina eso", considera
#include<limits>#include<assert.h>void foo(double a,double b ){
assert( a != b );}int main(){typedef std::numeric_limits<double>Info;doubleconst nan1 =Info::quiet_NaN();doubleconst nan2 =Info::quiet_NaN();
foo( nan1, nan2 );}
Compilación con g ++ (TDM-2 mingw32) 4.4.1:
C: \ test> escriba "C: \ Archivos de programa \ @commands \ gnuc.bat"
@rem -finput-charset = windows-1252
@ g ++ -O -pedantic -std = c ++ 98 -Wall -Wwrite-strings% * -Wno-long-long
C: \ prueba> gnuc x.cpp
C: \ test> a && echo funciona ... || echo! falló
trabajos...
C: \ prueba> gnuc x.cpp --matemáticas rápidas
C: \ test> a && echo funciona ... || echo! falló
Error de aserción: a! = B, archivo x.cpp, línea 6
Esta aplicación ha solicitado el tiempo de ejecución para terminarlo de una manera inusual.
Póngase en contacto con el equipo de soporte de la aplicación para obtener más información.
!ha fallado
C: \ prueba> _
@Alf: Su ejemplo funciona como se esperaba para mí tanto en Mac OS X como en Linux en varias versiones de g ++ entre 4.0 y 4.5. La documentación de la -ffast-mathopción dice explícitamente que puede dar como resultado resultados incorrectos para programas que dependen de una implementación exacta si las reglas / especificaciones IEEE o ISO para funciones matemáticas. Sin esa opción habilitada, el uso x != xes una forma perfectamente válida y portátil de probar NaN.
Adam Rosenfield
66
@ Adam: lo que te falta es que el estándar C ++ no requiere representación IEEE o matemática para las carrozas. Por lo que dice la página del manual, gcc -ffast-mathsigue siendo una implementación de C ++ conforme (bueno, suponiendo que sea numeric_limits::is_iec559correcta, lo es, aunque Alf sugiere que no): el código de C ++ que se basa en IEEE no es C ++ portátil y no tiene derecho esperar implementaciones para proporcionarlo.
Steve Jessop
55
Y Alf tiene razón, prueba rápida en gcc 4.3.4 y is_iec559es cierto con -ffast-math. Entonces, el problema aquí es que los documentos de GCC -ffast-mathsolo dicen que no es IEEE / ISO para las funciones matemáticas, mientras que deberían decir que no es C ++, porque su implementación numeric_limitsestá simplificada. Supongo que GCC no siempre puede decir en el momento en que se define la plantilla, si el backend eventual realmente tiene flotadores conformes, por lo que ni siquiera lo intenta. IIRC hay problemas similares en la lista de errores pendientes para la conformidad C99 de GCC.
Steve Jessop
1
@Alf, @Steve, no sabía que el estándar C ++ no tiene especificación sobre valores de punto flotante. Es bastante impactante para mí. Se ve mejor manejando IEEE 754 y NaN como una extensión específica de plataforma en lugar de estándar. ¿No es así? ¿Y puedo esperar cualquier tipo de isnan () o IEEE754 agregado en C ++ 0x?
Eonil
3
@Eonil: C ++ 0x todavía tiene, por ejemplo, "La representación del valor de los tipos de punto flotante está definida por la implementación". C y C ++ tienen como objetivo admitir implementaciones en máquinas sin hardware de punto flotante, y los flotadores IEEE 754 adecuados pueden ser bastante más lentos de emular que las alternativas razonablemente precisas. La teoría es que puede afirmar is_iec559si necesita IEEE, en la práctica eso no parece funcionar en GCC. C ++ 0x tiene una isnanfunción, pero dado que GCC no se implementa correctamente is_iec559ahora, supongo que tampoco lo hará en C ++ 0x, y -ffast-mathbien podría romperlo isnan.
Steve Jessop
39
Hay un std :: isnan si su compilador admite extensiones c99, pero no estoy seguro de si mingw lo hace.
Aquí hay una pequeña función que debería funcionar si su compilador no tiene la función estándar:
bool custom_isnan(double var){volatiledouble d = var;return d != d;}
Al hacerlo, existe la posibilidad de que el compilador optimice la comparación, siempre regresando verdadero.
CTT
23
No, no lo hay. Un compilador que hace eso está roto. También podría decir que existe la posibilidad de que la biblioteca estándar isnandevuelva el resultado incorrecto. Técnicamente cierto, el compilador podría tener errores, pero en la práctica, no va a suceder. Igual que var != var. Funciona porque así es como se definen los valores de punto flotante IEEE.
jalf
29
si se establece -ffast-math, isnan () no podrá devolver el resultado correcto para gcc. Por supuesto, esta optimización está documentada como rompiendo la semántica de IEEE ...
Matthew Herrmann
Si se establece -ffast-math, entonces el compilador tiene errores. O, más bien, si se establece -ffast-math, todas las apuestas están apagadas y de todos modos no puede confiar en NaNs.
Adrian Ratnapala
25
Puede usar numeric_limits<float>::quiet_NaN( )definido en la limitsbiblioteca estándar para probar. Hay una constante separada definida para double.
#include<iostream>#include<math.h>#include<limits>usingnamespace std;int main(){
cout <<"The quiet NaN for type float is: "<< numeric_limits<float>::quiet_NaN()<< endl;float f_nan = numeric_limits<float>::quiet_NaN();if( isnan(f_nan)){
cout <<"Float was Not a Number: "<< f_nan << endl;}return0;}
No sé si esto funciona en todas las plataformas, ya que solo probé con g ++ en Linux.
Sin embargo, tenga cuidado: parece haber un error en numeric_limits en GCC versión 3.2.3, ya que devuelve 0.0 para quiet_NaN. Las versiones posteriores de GCC están bien en mi experiencia.
Nathan Kitchen
@Nathan: Es bueno saberlo. Estoy usando la versión 4.3.2, así que estoy fuera de peligro.
Bill the Lizard
18
Puede usar la isnan()función, pero debe incluir la biblioteca matemática de C.
#include<cmath>
Como esta función es parte de C99, no está disponible en todas partes. Si su proveedor no proporciona la función, también puede definir su propia variante para la compatibilidad.
¡Estaba usando <cmath> y no hay isnan en él! dicho sea de paso descubrí que no es una isnanen <math.h>
Hasen
1
Como dije, esto es parte de C99. Como C99 no es parte de ningún estándar actual de C ++, proporcioné la alternativa. Pero como es probable que isnan () se incluya en un próximo estándar de C ++, puse una directiva #ifndef a su alrededor.
raimue
12
El siguiente código utiliza la definición de NAN (conjunto de bits de exponente, al menos un conjunto de bits fraccional) y supone que sizeof (int) = sizeof (float) = 4. Puede buscar NAN en Wikipedia para obtener más detalles.
Creo que esto también funcionaría en grandes plataformas endian. El literal 0x7fffffffsimplemente se quedaría en la memoria como ff ff ff 7f. valuetiene el mismo orden que tiene 0x7f800000, por lo que todas las operaciones se alinean (no hay intercambio de bytes). Me interesaría si alguien pudiera probar esto en una gran plataforma endian.
Bryan W. Wagner
0x7fff1234También es un NaN. Así es0xffffffff
Steve Hollasch
12
prevención nan
Mi respuesta a esta pregunta es no usar controles retroactivos paranan . Utilice controles preventivos para las divisiones del formulario en su 0.0/0.0lugar.
#include<float.h>float x=0.f;// I'm gonna divide by x!if(!x )// Wait! Let me check if x is 0
x = FLT_MIN ;// oh, since x was 0, i'll just make it really small instead.float y =0.f/ x ;// whew, `nan` didn't appear.
nanresultados de la operación 0.f/0.f, o 0.0/0.0. nanEs un terrible enemigo de la estabilidad de su código que debe ser detectado y evitado con mucho cuidado 1 . Las propiedades de naneso son diferentes de los números normales:
nanes tóxico, (5 * nan= nan)
nanno es igual a nada, ni siquiera a sí mismo ( nan! = nan)
nanno mayor que nada ( nan!> 0)
nanno es menos que nada ( nan! <0)
Las últimas 2 propiedades enumeradas son contra-lógicas y darán como resultado un comportamiento extraño del código que se basa en comparaciones con un nannúmero (la 3ra última propiedad también es extraña pero probablemente nunca verá x != x ?en su código (a menos que esté verificando) para nan (poco confiable))).
En mi propio código, noté que los nanvalores tienden a producir errores difíciles de encontrar. (Tenga en cuenta que este no es el caso para info -inf. ( -inf<0) devuelve TRUE, (0 < inf) devuelve VERDADERO e incluso ( -inf< inf) devuelve VERDADERO. Entonces, en mi experiencia, el comportamiento del código a menudo sigue siendo el deseado).
que hacer bajo nan
Lo que desea que ocurra 0.0/0.0debe manejarse como un caso especial , pero lo que haga debe depender de los números que espera obtener del código.
En el ejemplo anterior, el resultado de ( 0.f/FLT_MIN) será 0, básicamente. Es posible que desee 0.0/0.0generar en su HUGElugar. Entonces,
float x=0.f, y=0.f, z;if(!x &&!y )// 0.f/0.f case
z = FLT_MAX ;// biggest float possibleelse
z = y/x ;// regular division.
Entonces, en lo anterior, si x fuera 0.f, infresultaría (que tiene un comportamiento bastante bueno / no destructivo como se mencionó anteriormente en realidad).
1 Las comprobaciones de nanvía a x != xveces no son confiables ( x != xalgunos compiladores optimizadores las eliminan y rompen el cumplimiento de IEEE, específicamente cuando el -ffast-mathconmutador está habilitado).
Gracias por señalar esto; Una programación como esa definitivamente ayudaría con el problema como tal. Pero la próxima vez, intente no abusar demasiado de las funciones de formato de texto. Cambiar el tamaño de fuente, el peso y el estilo de este tipo hacen que sea realmente difícil de leer.
Magnus
44
Tenga en cuenta que 0.0 / 0.0 no es la única operación que podría resultar en un NaN. La raíz cuadrada de un número negativo devuelve NaN. El coseno de + infinito también devuelve NaN. la operación acos (x) donde x no está en el rango [0, pi] también puede dar como resultado NaN. En pocas palabras, hay que tener mucho cuidado para observar también estas operaciones potencialmente riesgosas, no solo a 0.0 / 0.0.
Boris Dalstein
Totalmente de acuerdo con Boris. En mi experiencia, NaN prácticamente siempre proviene de algo así como sqrt (-1,302e-53), es decir, los resultados de cálculo intermedios cercanos a cero se introducen en sqrt sin verificar la negatividad.
hans_meine
1
"Prevenir los NaN" significa que necesita entrar en todas las operaciones aritméticas básicas, no solo en la división. Deberá tener cuidado con ∞ / ∞, 0 * ∞, ∞% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0, entre muchos otros. Ser "preventivo" con operaciones aritméticas tan básicas significa que limitará por completo su rendimiento (y probablemente perderá casos adicionales en los que no pensó).
Steve Hollasch
11
A partir de C ++ 14, hay varias formas de probar si un número de coma flotante value es un NaN.
De estas formas, solo la comprobación de los bits de la representación del número funciona de manera confiable, como se señaló en mi respuesta original. En particular, std::isnany la verificación propuesta a menudo v != v, no funciona de manera confiable y no debe usarse, para que su código deje de funcionar correctamente cuando alguien decida que se necesita optimización de punto flotante y le pide al compilador que lo haga. Esta situación puede cambiar, los compiladores pueden ser más conformes, pero para este problema que no ha sucedido en los 6 años desde la respuesta original.
Durante unos 6 años, mi respuesta original fue la solución seleccionada para esta pregunta, que estaba bien. Pero recientemente v != vse ha seleccionado una respuesta altamente votada que recomienda la prueba poco confiable . De ahí esta respuesta adicional más actualizada (ahora tenemos los estándares C ++ 11 y C ++ 14, y C ++ 17 en el horizonte).
Las principales formas de verificar la NaN-ness, a partir de C ++ 14, son:
std::isnan(value) )
es la forma de biblioteca estándar prevista desde C ++ 11. isnanaparentemente entra en conflicto con la macro Posix del mismo nombre, pero en la práctica eso no es un problema. El problema principal es que cuando se solicita la optimización aritmética de punto flotante, con al menos un compilador principal, a saber, g ++,std::isnan devuelve el falseargumento NaN .
(fpclassify(value) == FP_NAN) )
Sufre el mismo problema que std::isnan, es decir, no es confiable.
(value != value) )
Recomendado en muchas respuestas SO. Sufre del mismo problema questd::isnan , es decir, no es confiable.
(value == Fp_info::quiet_NaN()) )
Esta es una prueba que con el comportamiento estándar no debería detectar NaNs, pero que con el comportamiento optimizado podría detectar NaNs (debido a que el código optimizado solo compara las representaciones de nivel de bit directamente), y tal vez combinado con otra forma de cubrir el comportamiento no optimizado estándar , podría detectar NaN de manera confiable. Desafortunadamente resultó no funcionar de manera confiable.
(ilogb(value) == FP_ILOGBNAN) )
Sufre del mismo problema que std::isnan, es decir, no es confiable.
isunordered(1.2345, value) )
Sufre del mismo problema que std::isnan, es decir, no es confiable.
is_ieee754_nan( value ) )
Esta no es una función estándar. Está comprobando los bits de acuerdo con el estándar IEEE 754. Es completamente confiable pero el código depende de alguna manera del sistema.
En el siguiente código de prueba completo, "éxito" es si una expresión informa Nanness del valor. Para la mayoría de las expresiones, esta medida de éxito, el objetivo de detectar NaNs y solo NaNs, corresponde a su semántica estándar. Para el(value == Fp_info::quiet_NaN()) )Sin embargo, expresión, el comportamiento estándar es que no funciona como un detector de NaN.
Resultados con g ++ (tenga en cuenta de nuevo que el comportamiento estándar de (value == Fp_info::quiet_NaN())es que no funciona como un detector de NaN, solo es de gran interés práctico aquí):
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> g ++ --version | encontrar "++"
g ++ (x86_64-win32-sjlj-rev1, construido por el proyecto MinGW-W64) 6.3.0
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> g ++ foo.cpp && a
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = verdadero éxito
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 0x0100)) = verdadero Éxito
u = 3.14, ((fpclassify (value) == 0x0100)) = falso Éxito
w = inf, ((fpclassify (value) == 0x0100)) = falso Éxito
v = nan, ((valor! = valor)) = verdadero Éxito
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = falso FALLIDO
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
w = inf, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
v = nan, ((ilogb (value) == ((int) 0x80000000))) = verdadero éxito
u = 3.14, ((ilogb (valor) == ((int) 0x80000000))) = falso Éxito
w = inf, ((ilogb (value) == ((int) 0x80000000))) = falso Éxito
v = nan, (no está ordenado (1.2345, valor)) = verdadero Éxito
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> g ++ foo.cpp -ffast-math && a
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = falso FALLIDO
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 0x0100)) = falso FALLIDO
u = 3.14, ((fpclassify (value) == 0x0100)) = falso Éxito
w = inf, ((fpclassify (value) == 0x0100)) = falso Éxito
v = nan, ((valor! = valor)) = falso FALLIDO
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = verdadero Éxito
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = verdadero FALLIDO
w = inf, ((valor == Fp_info :: quiet_NaN ())) = verdadero FALLIDO
v = nan, ((ilogb (value) == ((int) 0x80000000))) = verdadero éxito
u = 3.14, ((ilogb (valor) == ((int) 0x80000000))) = falso Éxito
w = inf, ((ilogb (value) == ((int) 0x80000000))) = falso Éxito
v = nan, (no está ordenado (1.2345, valor)) = falso FALLIDO
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> _
Resultados con Visual C ++:
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> cl / nologo- 2> y 1 | encontrar "++"
Microsoft (R) C / C ++ Optimizing Compiler versión 19.00.23725 para x86
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> cl foo.cpp / Feb && b
foo.cpp
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = verdadero éxito
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 2)) = verdadero éxito
u = 3.14, ((fpclassify (value) == 2)) = falso Éxito
w = inf, ((fpclassify (value) == 2)) = falso Éxito
v = nan, ((valor! = valor)) = verdadero Éxito
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = falso FALLIDO
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
w = inf, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
v = nan, ((ilogb (value) == 0x7fffffff)) = verdadero Éxito
u = 3.14, ((ilogb (valor) == 0x7fffffff)) = falso Éxito
w = inf, ((ilogb (valor) == 0x7fffffff)) = verdadero FALLIDO
v = nan, (no está ordenado (1.2345, valor)) = verdadero Éxito
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> cl foo.cpp / Feb / fp: rápido && b
foo.cpp
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = verdadero éxito
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 2)) = verdadero éxito
u = 3.14, ((fpclassify (value) == 2)) = falso Éxito
w = inf, ((fpclassify (value) == 2)) = falso Éxito
v = nan, ((valor! = valor)) = verdadero Éxito
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = falso FALLIDO
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
w = inf, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
v = nan, ((ilogb (value) == 0x7fffffff)) = verdadero Éxito
u = 3.14, ((ilogb (valor) == 0x7fffffff)) = falso Éxito
w = inf, ((ilogb (valor) == 0x7fffffff)) = verdadero FALLIDO
v = nan, (no está ordenado (1.2345, valor)) = verdadero Éxito
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> _
Resumiendo los resultados anteriores, solo las pruebas directas de la representación a nivel de bit, utilizando la is_ieee754_nanfunción definida en este programa de prueba, funcionaron de manera confiable en todos los casos tanto con g ++ como con Visual C ++.
Anexo:
Después de publicar lo anterior, me di cuenta de otra posible prueba para NaN, mencionada en otra respuesta aquí, a saber ((value < 0) == (value >= 0)). Resultó funcionar bien con Visual C ++ pero falló con la -ffast-mathopción de g ++ . Solo las pruebas directas de patrones de bits funcionan de manera confiable.
inlineboolIsNan(float f){constuint32 u =*(uint32*)&f;return(u&0x7F800000)==0x7F800000&&(u&0x7FFFFF);// Both NaN and qNan.}inlineboolIsNan(double d){constuint64 u =*(uint64*)&d;return(u&0x7FF0000000000000ULL)==0x7FF0000000000000ULL&&(u&0xFFFFFFFFFFFFFULL);}
Esto funciona si sizeof(int)es 4 y sizeof(long long)es 8.
Durante el tiempo de ejecución es solo una comparación, los moldes no toman tiempo. Simplemente cambia la configuración de las banderas de comparación para verificar la igualdad.
También tenga en cuenta que está limitado a la representación IEEE 754.
Saludos y hth. - Alf
Tenga en cuenta que este reparto rompe la estricta regla de alias de g ++, y que se sabe que el compilador hace Unmentionable Things ™ cuando detecta UB formal. En lugar de conversiones eficientes, con g ++ debe usar memcpy, a través de una matriz de bytes, para estar seguro. Codifique eso en mi respuesta # 2 .
Saludos y hth. - Alf
4
Una posible solución que no dependería de la representación IEEE específica para NaN utilizada sería la siguiente:
template<class T>bool isnan( T f ){
T _nan =(T)0.0/(T)0.0;return0== memcmp((void*)&f,(void*)&_nan,sizeof(T));}
El punto flotante de precisión simple tiene más de 8 millones de representaciones de bits legítimas y diferentes para NaN, por lo que deberá agregar algunas comparaciones más. :)
Steve Hollasch
4
Teniendo en cuenta que (x! = X) no siempre está garantizado para NaN (como si usara la opción -ffast-math), he estado usando:
#define IS_NAN(x)(((x)<0)==((x)>=0))
Los números no pueden ser ambos <0 y> = 0, por lo que realmente esta verificación solo pasa si el número no es menor, ni mayor o igual que cero. Que básicamente no es ningún número, o NaN.
También puede usar esto si prefiere:
#define IS_NAN(x)(!((x)<0)&&!((x)>=0)
Sin embargo, no estoy seguro de cómo esto se ve afectado por las matemáticas rápidas, por lo que su millaje puede variar.
Esto es realmente defectuoso de la misma manera que f != fes defectuoso también. He visto llvm optimizando una pieza de código casi idéntica. El optimizador puede propagar la información sobre la primera comparación y descubrir que la segunda comparación puede nunca ser cierta si la primera lo es. (si el compilador obedece estrictamente las reglas de IEEE f != fes mucho más simple de todos modos)
En cuanto a mí, la solución podría ser una macro para hacerlo explícitamente en línea y, por lo tanto, lo suficientemente rápido. También funciona para cualquier tipo de flotador. Se basa en el hecho de que el único caso cuando un valor no es igual a sí mismo es cuando el valor no es un número.
Me parece que el mejor enfoque verdaderamente multiplataforma sería usar una unión y probar el patrón de bits del doble para verificar si hay NaN.
No he probado a fondo esta solución, y puede haber una forma más eficiente de trabajar con los patrones de bits, pero creo que debería funcionar.
#include<stdint.h>#include<stdio.h>unionNaN{uint64_t bits;double num;};int main(){//Test if a double is NaNdouble d =0.0/0.0;unionNaN n;
n.num = d;if((n.bits |0x800FFFFFFFFFFFFF)==0xFFFFFFFFFFFFFFFF){
printf("NaN: %f", d);}return0;}
Tenga en cuenta que "es un comportamiento indefinido leer del miembro del sindicato que no se escribió más recientemente". Por lo tanto, este uso de un unionjuego de palabras entre dos tipos puede no funcionar como se desea (: sad_panda :). La forma correcta (aunque en realidad no es tan portátil como se desea) sería evitar la unión por completo y hacer una memoria desde doubleuna uint64_tvariable diferente , luego hacer la prueba usando esa variable auxiliar.
Eljay
0
En x86-64 puede tener métodos extremadamente rápidos para verificar NaN e infinito, que funcionan independientemente de la -ffast-mathopción del compilador. ( f != f, std::isnan, std::isinfSiempre producen falsecon -ffast-math).
Las pruebas de NaN, infinito y números finitos se pueden hacer fácilmente al verificar el exponente máximo. infinito es máximo exponente con cero mantisa, NaN es máximo exponente y no cero mantisa. El exponente se almacena en los siguientes bits después del bit de signo superior, de modo que simplemente podemos desplazarnos a la izquierda para deshacernos del bit de signo y hacer que el exponente sea el bit superior, no operator&es necesario enmascarar ( ):
staticinlineuint64_t load_ieee754_rep(double a){uint64_t r;static_assert(sizeof r ==sizeof a,"Unexpected sizes.");
std::memcpy(&r,&a,sizeof a);// Generates movq instruction.return r;}staticinlineuint32_t load_ieee754_rep(float a){uint32_t r;static_assert(sizeof r ==sizeof a,"Unexpected sizes.");
std::memcpy(&r,&a,sizeof a);// Generates movd instruction.return r;}constexpruint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);constexpruint32_t inf_float_shl1 = UINT32_C(0xff000000);// The shift left removes the sign bit. The exponent moves into the topmost bits,// so that plain unsigned comparison is enough.staticinlinebool isnan2(double a){return load_ieee754_rep(a)<<1> inf_double_shl1;}staticinlinebool isinf2(double a){return load_ieee754_rep(a)<<1== inf_double_shl1;}staticinlinebool isfinite2(double a){return load_ieee754_rep(a)<<1< inf_double_shl1;}staticinlinebool isnan2(float a){return load_ieee754_rep(a)<<1> inf_float_shl1;}staticinlinebool isinf2(float a){return load_ieee754_rep(a)<<1== inf_float_shl1;}staticinlinebool isfinite2(float a){return load_ieee754_rep(a)<<1< inf_float_shl1;}
Las stdversiones de isinfyisfinite cargan 2 double/floatconstantes del .datasegmento y, en el peor de los casos, pueden causar 2 errores de caché de datos. Las versiones anteriores no cargan ningún dato, inf_double_shl1y las inf_float_shl1constantes se codifican como operandos inmediatos en las instrucciones de ensamblaje.
Más rápido isnan2son solo 2 instrucciones de montaje:
Utiliza el hecho de que la ucomisdinstrucción establece el indicador de paridad si algún argumento es NaN. Así es como std::isnanfunciona cuando no -ffast-mathse especifica ninguna opción.
El estándar IEEE dice que cuando el exponente es todo 1sy la mantisa no es cero, el número es a NaN. Doble es 1bit de signo, 11bits de exponente y 52bits de mantisa. Haz un poco de verificación.
Como los comentarios anteriores indican a! = A no funcionará en g ++ y algunos otros compiladores, pero este truco debería funcionar. Puede que no sea tan eficiente, pero sigue siendo una forma:
Básicamente, en g ++ (aunque no estoy seguro de otros) printf imprime 'nan' en formatos% d o% .f si la variable no es un entero / flotante válido. Por lo tanto, este código verifica que el primer carácter de la cadena sea 'n' (como en "nan")
¿No causaría eso un desbordamiento del búfer si a = 234324.0f?
Mazyod
Sí, o 340282346638528859811704183484516925440.000si a = FLT_MAX. Tendría que usar char s[7]; sprintf(s, "%.0g", a);, que serán 6 canales si a=-FLT_MAX, o-3e+38
bobobobo
-3
Esto detecta el infinito y también NaN en Visual Studio comprobando que está dentro de los límites dobles:
//#include <float.h>double x, y =-1.1; x = sqrt(y);if(x >= DBL_MIN && x <= DBL_MAX )
cout <<"DETECTOR-2 of errors FAILS"<< endl;else
cout <<"DETECTOR-2 of errors OK"<< endl;
Verifique la definición de FLT_MIN, DBL_MINy LDBL_MINmás cuidadosamente. Estos se definen como los valores normalizados más pequeños para cada tipo. Por ejemplo, la precisión simple tiene más de 8 millones de valores denorm legítimos que son mayores que cero y menores que FLT_MIN(y no son NaN).
nan
's en su código.nan
Esto puede ser terriblemente destructivo para su programa, si se permite que prolifere, puede introducir errores difíciles de encontrar. Esto se debe a quenan
es tóxico, (5 *nan
=nan
),nan
no es igual a nada (nan
! =nan
),nan
No es mayor que nada (nan
!> 0),nan
no es menor que nada (nan
! <0).Respuestas:
Según el estándar IEEE, los valores de NaN tienen la extraña propiedad de que las comparaciones que los involucran son siempre falsas. Es decir, para un flotador f,
f != f
será verdadero solo si f es NaN.Tenga en cuenta que, como han señalado algunos comentarios a continuación, no todos los compiladores respetan esto al optimizar el código.
Para cualquier compilador que afirme utilizar el punto flotante IEEE, este truco debería funcionar. Pero no puedo garantizar que va a funcionar en la práctica. Consulte con su compilador, si tiene dudas.
fuente
-ffast-math
opción dice explícitamente que puede dar como resultado resultados incorrectos para programas que dependen de una implementación exacta si las reglas / especificaciones IEEE o ISO para funciones matemáticas. Sin esa opción habilitada, el usox != x
es una forma perfectamente válida y portátil de probar NaN.x != x
es válida sin esa opción no sigue lógicamente. Puede ser cierto para una versión particular de g ++, o no. de todos modos, generalmente no tiene forma de garantizar que no se utilizará la opción de matemáticas rápidas.-ffast-math
opción), entoncesx != x
es una solución válida y portátil. Incluso puede probar-ffast-math
probando la__FAST_MATH__
macro y cambiar a una implementación diferente en ese caso (por ejemplo, usar uniones y twiddling de bits).No hay ninguna
isnan()
función disponible en la Biblioteca estándar de C ++ actual. Fue introducido en C99 y definido como una macro, no una función. Los elementos de la biblioteca estándar definidos por C99 no forman parte del estándar actual C ++ ISO / IEC 14882: 1998 ni su actualización ISO / IEC 14882: 2003.En 2005 se propuso el Informe técnico 1. El TR1 trae compatibilidad con C99 a C ++. A pesar de que nunca se ha adoptado oficialmente para convertirse en estándar C ++, muchas (las implementaciones de GCC 4.0+ o Visual C ++ 9.0+ C ++ proporcionan características TR1, todas ellas o solo algunas (Visual C ++ 9.0 no proporciona funciones matemáticas C99) .
Si TR1 está disponible, entonces
cmath
incluye elementos C99 comoisnan()
,isfinite()
etc., pero se definen como funciones, no macros, generalmente en elstd::tr1::
espacio de nombres, aunque muchas implementaciones (es decir, GCC 4+ en Linux o en XCode en Mac OS X 10.5+) los inyectan directamente astd::
, por lo questd::isnan
está bien definido.Además, algunas implementaciones de C ++ todavía hacen que C99
isnan()
macro esté disponible para C ++ (incluido a través decmath
omath.h
), lo que puede causar más confusión y los desarrolladores pueden asumir que es un comportamiento estándar.Una nota sobre Viusal C ++, como se mencionó anteriormente, no proporciona
std::isnan
ningunostd::tr1::isnan
, pero proporciona una función de extensión definida como la_isnan()
que ha estado disponible desde Visual C ++ 6.0En XCode, hay aún más diversión. Como se mencionó, GCC 4+ define
std::isnan
. Para versiones anteriores del compilador y la biblioteca de XCode, parece que (aquí hay una discusión relevante ), no he tenido la oportunidad de verificarme) se definen dos funciones,__inline_isnand()
en Intel y__isnand()
en Power PC.fuente
std::isnan
que ahora forma parte del estándar C ++ 11 y el soporte se ha extendido. std :: isnan fue implementado en Visual Studio comenzando con Visual Studio 2013. Tal vez conseguido @shuhalo a cargo :-)Primera solución: si está utilizando C ++ 11
Como se preguntó esto, hubo un poco de nuevos desarrollos: es importante saber que
std::isnan()
es parte de C ++ 11Sinopsis
Definido en encabezado
<cmath>
Determina si el número de coma flotante dado arg no es un número (
NaN
).Parámetros
arg
: valor de coma flotanteValor de retorno
true
si arg esNaN
, de lofalse
contrarioReferencia
http://en.cppreference.com/w/cpp/numeric/math/isnan
Tenga en cuenta que esto es incompatible con -fast-math si usa g ++, consulte a continuación otras sugerencias.
Otras soluciones: si utiliza herramientas que no cumplen con C ++ 11
Para C99, en C, esto se implementa como una macro
isnan(c)
que devuelve un valor int. El tipo dex
será flotante, doble o doble largo.Varios proveedores pueden o no incluir o no una función
isnan()
.La forma supuestamente portátil de verificar
NaN
es usar la propiedad IEEE 754 queNaN
no es igual a sí misma, es decirx == x
, será falsa porx
serNaN
.Sin embargo, la última opción puede no funcionar con todos los compiladores y algunas configuraciones (particularmente las configuraciones de optimización), por lo que, en último recurso, siempre puede verificar el patrón de bits ...
fuente
std::isnan
sigue siendo una recomendación incorrecta a partir de febrero de 2017, ya que no funciona con la optimización de punto flotante de g ++.x != x
yisnan
están obligados a trabajar para cumplir con IEEE 754. Con respecto a esto último, el estándar IEEE 754-2008 establece que "Las implementaciones proporcionarán las siguientes operaciones no computacionales para todos los formatos aritméticos compatibles" y "isNaN (x) es verdadero si y solo si x es un NaN". Para verificar la conformidad que requiere el estándaris754version1985()
yis754version2008()
, en su lugar, donde C ++ ofrecestd::numeric_limits<Fp>::is_iec559()
(IEC 559 es el mismo estándar). Desafortunadamente con la-ffast-math
optimización, por ejemplo, g ++ afirma conformidad pero no es conforme.También hay una biblioteca de solo encabezado presente en Boost que tiene herramientas ordenadas para lidiar con los tipos de datos de punto flotante
Obtiene las siguientes funciones:
Si tiene tiempo, eche un vistazo a todo el kit de herramientas matemáticas de Boost, tiene muchas herramientas útiles y está creciendo rápidamente.
Además, cuando se trata de puntos flotantes y no flotantes, puede ser una buena idea mirar las conversiones numéricas .
fuente
Hay tres formas de "oficiales": POSIX
isnan
macro , C ++ 0xisnan
plantilla de función , o Visual C ++_isnan
función .Desafortunadamente, es poco práctico detectar cuál de ellos usar.
Y desafortunadamente, no hay una manera confiable de detectar si tiene representación IEEE 754 con NaNs. La biblioteca estándar ofrece una forma oficial (
numeric_limits<double>::is_iec559
). Pero en la práctica, los compiladores como g ++ lo arruinan.En teoría, uno podría usar simplemente
x != x
, pero los compiladores como g ++ y visual c ++ lo arruinan.Por lo tanto, al final, pruebe los patrones de bits NaN específicos , suponiendo (¡y con suerte haciendo cumplir, en algún momento!) Una representación particular como IEEE 754.
EDITAR : como un ejemplo de "compiladores como g ++ ... arruina eso", considera
Compilación con g ++ (TDM-2 mingw32) 4.4.1:
fuente
-ffast-math
opción dice explícitamente que puede dar como resultado resultados incorrectos para programas que dependen de una implementación exacta si las reglas / especificaciones IEEE o ISO para funciones matemáticas. Sin esa opción habilitada, el usox != x
es una forma perfectamente válida y portátil de probar NaN.gcc -ffast-math
sigue siendo una implementación de C ++ conforme (bueno, suponiendo que seanumeric_limits::is_iec559
correcta, lo es, aunque Alf sugiere que no): el código de C ++ que se basa en IEEE no es C ++ portátil y no tiene derecho esperar implementaciones para proporcionarlo.is_iec559
es cierto con-ffast-math
. Entonces, el problema aquí es que los documentos de GCC-ffast-math
solo dicen que no es IEEE / ISO para las funciones matemáticas, mientras que deberían decir que no es C ++, porque su implementaciónnumeric_limits
está simplificada. Supongo que GCC no siempre puede decir en el momento en que se define la plantilla, si el backend eventual realmente tiene flotadores conformes, por lo que ni siquiera lo intenta. IIRC hay problemas similares en la lista de errores pendientes para la conformidad C99 de GCC.is_iec559
si necesita IEEE, en la práctica eso no parece funcionar en GCC. C ++ 0x tiene unaisnan
función, pero dado que GCC no se implementa correctamenteis_iec559
ahora, supongo que tampoco lo hará en C ++ 0x, y-ffast-math
bien podría romperloisnan
.Hay un std :: isnan si su compilador admite extensiones c99, pero no estoy seguro de si mingw lo hace.
Aquí hay una pequeña función que debería funcionar si su compilador no tiene la función estándar:
fuente
isnan
devuelva el resultado incorrecto. Técnicamente cierto, el compilador podría tener errores, pero en la práctica, no va a suceder. Igual quevar != var
. Funciona porque así es como se definen los valores de punto flotante IEEE.Puede usar
numeric_limits<float>::quiet_NaN( )
definido en lalimits
biblioteca estándar para probar. Hay una constante separada definida paradouble
.No sé si esto funciona en todas las plataformas, ya que solo probé con g ++ en Linux.
fuente
Puede usar la
isnan()
función, pero debe incluir la biblioteca matemática de C.Como esta función es parte de C99, no está disponible en todas partes. Si su proveedor no proporciona la función, también puede definir su propia variante para la compatibilidad.
fuente
isnan
en <math.h>El siguiente código utiliza la definición de NAN (conjunto de bits de exponente, al menos un conjunto de bits fraccional) y supone que sizeof (int) = sizeof (float) = 4. Puede buscar NAN en Wikipedia para obtener más detalles.
bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }
fuente
0x7fffffff
simplemente se quedaría en la memoria comoff ff ff 7f
.value
tiene el mismo orden que tiene0x7f800000
, por lo que todas las operaciones se alinean (no hay intercambio de bytes). Me interesaría si alguien pudiera probar esto en una gran plataforma endian.0x7fff1234
También es un NaN. Así es0xffffffff
prevención nan
Mi respuesta a esta pregunta es no usar controles retroactivos para
nan
. Utilice controles preventivos para las divisiones del formulario en su0.0/0.0
lugar.nan
resultados de la operación0.f/0.f
, o0.0/0.0
.nan
Es un terrible enemigo de la estabilidad de su código que debe ser detectado y evitado con mucho cuidado 1 . Las propiedades denan
eso son diferentes de los números normales:nan
es tóxico, (5 *nan
=nan
)nan
no es igual a nada, ni siquiera a sí mismo (nan
! =nan
)nan
no mayor que nada (nan
!> 0)nan
no es menos que nada (nan
! <0)Las últimas 2 propiedades enumeradas son contra-lógicas y darán como resultado un comportamiento extraño del código que se basa en comparaciones con un
nan
número (la 3ra última propiedad también es extraña pero probablemente nunca veráx != x ?
en su código (a menos que esté verificando) para nan (poco confiable))).En mi propio código, noté que los
nan
valores tienden a producir errores difíciles de encontrar. (Tenga en cuenta que este no es el caso parainf
o-inf
. (-inf
<0) devuelveTRUE
, (0 <inf
) devuelve VERDADERO e incluso (-inf
<inf
) devuelve VERDADERO. Entonces, en mi experiencia, el comportamiento del código a menudo sigue siendo el deseado).que hacer bajo nan
Lo que desea que ocurra
0.0/0.0
debe manejarse como un caso especial , pero lo que haga debe depender de los números que espera obtener del código.En el ejemplo anterior, el resultado de (
0.f/FLT_MIN
) será0
, básicamente. Es posible que desee0.0/0.0
generar en suHUGE
lugar. Entonces,Entonces, en lo anterior, si x fuera
0.f
,inf
resultaría (que tiene un comportamiento bastante bueno / no destructivo como se mencionó anteriormente en realidad).Recuerde, la división de enteros por 0 causa una excepción de tiempo de ejecución . Por lo tanto, siempre debe verificar la división de enteros por 0. El hecho de que
0.0/0.0
se evalúe silenciosamentenan
no significa que pueda ser vago y no verificar0.0/0.0
antes de que suceda.1 Las comprobaciones de
nan
vía ax != x
veces no son confiables (x != x
algunos compiladores optimizadores las eliminan y rompen el cumplimiento de IEEE, específicamente cuando el-ffast-math
conmutador está habilitado).fuente
A partir de C ++ 14, hay varias formas de probar si un número de coma flotante
value
es un NaN.De estas formas, solo la comprobación de los bits de la representación del número funciona de manera confiable, como se señaló en mi respuesta original. En particular,
std::isnan
y la verificación propuesta a menudov != v
, no funciona de manera confiable y no debe usarse, para que su código deje de funcionar correctamente cuando alguien decida que se necesita optimización de punto flotante y le pide al compilador que lo haga. Esta situación puede cambiar, los compiladores pueden ser más conformes, pero para este problema que no ha sucedido en los 6 años desde la respuesta original.Durante unos 6 años, mi respuesta original fue la solución seleccionada para esta pregunta, que estaba bien. Pero recientemente
v != v
se ha seleccionado una respuesta altamente votada que recomienda la prueba poco confiable . De ahí esta respuesta adicional más actualizada (ahora tenemos los estándares C ++ 11 y C ++ 14, y C ++ 17 en el horizonte).Las principales formas de verificar la NaN-ness, a partir de C ++ 14, son:
std::isnan(value) )
es la forma de biblioteca estándar prevista desde C ++ 11.
isnan
aparentemente entra en conflicto con la macro Posix del mismo nombre, pero en la práctica eso no es un problema. El problema principal es que cuando se solicita la optimización aritmética de punto flotante, con al menos un compilador principal, a saber, g ++,std::isnan
devuelve elfalse
argumento NaN .(fpclassify(value) == FP_NAN) )
Sufre el mismo problema que
std::isnan
, es decir, no es confiable.(value != value) )
Recomendado en muchas respuestas SO. Sufre del mismo problema que
std::isnan
, es decir, no es confiable.(value == Fp_info::quiet_NaN()) )
Esta es una prueba que con el comportamiento estándar no debería detectar NaNs, pero que con el comportamiento optimizado podría detectar NaNs (debido a que el código optimizado solo compara las representaciones de nivel de bit directamente), y tal vez combinado con otra forma de cubrir el comportamiento no optimizado estándar , podría detectar NaN de manera confiable. Desafortunadamente resultó no funcionar de manera confiable.
(ilogb(value) == FP_ILOGBNAN) )
Sufre del mismo problema que
std::isnan
, es decir, no es confiable.isunordered(1.2345, value) )
Sufre del mismo problema que
std::isnan
, es decir, no es confiable.is_ieee754_nan( value ) )
Esta no es una función estándar. Está comprobando los bits de acuerdo con el estándar IEEE 754. Es completamente confiable pero el código depende de alguna manera del sistema.
En el siguiente código de prueba completo, "éxito" es si una expresión informa Nanness del valor. Para la mayoría de las expresiones, esta medida de éxito, el objetivo de detectar NaNs y solo NaNs, corresponde a su semántica estándar. Para el
(value == Fp_info::quiet_NaN()) )
Sin embargo, expresión, el comportamiento estándar es que no funciona como un detector de NaN.Resultados con g ++ (tenga en cuenta de nuevo que el comportamiento estándar de
(value == Fp_info::quiet_NaN())
es que no funciona como un detector de NaN, solo es de gran interés práctico aquí):Resultados con Visual C ++:
Resumiendo los resultados anteriores, solo las pruebas directas de la representación a nivel de bit, utilizando la
is_ieee754_nan
función definida en este programa de prueba, funcionaron de manera confiable en todos los casos tanto con g ++ como con Visual C ++.Anexo:
Después de publicar lo anterior, me di cuenta de otra posible prueba para NaN, mencionada en otra respuesta aquí, a saber
((value < 0) == (value >= 0))
. Resultó funcionar bien con Visual C ++ pero falló con la-ffast-math
opción de g ++ . Solo las pruebas directas de patrones de bits funcionan de manera confiable.fuente
Esto funciona si
sizeof(int)
es 4 ysizeof(long long)
es 8.Durante el tiempo de ejecución es solo una comparación, los moldes no toman tiempo. Simplemente cambia la configuración de las banderas de comparación para verificar la igualdad.
fuente
memcpy
, a través de una matriz de bytes, para estar seguro. Codifique eso en mi respuesta # 2 .Una posible solución que no dependería de la representación IEEE específica para NaN utilizada sería la siguiente:
fuente
Teniendo en cuenta que (x! = X) no siempre está garantizado para NaN (como si usara la opción -ffast-math), he estado usando:
Los números no pueden ser ambos <0 y> = 0, por lo que realmente esta verificación solo pasa si el número no es menor, ni mayor o igual que cero. Que básicamente no es ningún número, o NaN.
También puede usar esto si prefiere:
Sin embargo, no estoy seguro de cómo esto se ve afectado por las matemáticas rápidas, por lo que su millaje puede variar.
fuente
f != f
es defectuoso también. He visto llvm optimizando una pieza de código casi idéntica. El optimizador puede propagar la información sobre la primera comparación y descubrir que la segunda comparación puede nunca ser cierta si la primera lo es. (si el compilador obedece estrictamente las reglas de IEEEf != f
es mucho más simple de todos modos)-ffast-math
opción de g ++ . Funciona con Visual C ++. Ver ( stackoverflow.com/a/42138465/464581 ).En cuanto a mí, la solución podría ser una macro para hacerlo explícitamente en línea y, por lo tanto, lo suficientemente rápido. También funciona para cualquier tipo de flotador. Se basa en el hecho de que el único caso cuando un valor no es igual a sí mismo es cuando el valor no es un número.
fuente
Esto funciona:
salida: isnan
fuente
Me parece que el mejor enfoque verdaderamente multiplataforma sería usar una unión y probar el patrón de bits del doble para verificar si hay NaN.
No he probado a fondo esta solución, y puede haber una forma más eficiente de trabajar con los patrones de bits, pero creo que debería funcionar.
fuente
union
juego de palabras entre dos tipos puede no funcionar como se desea (: sad_panda :). La forma correcta (aunque en realidad no es tan portátil como se desea) sería evitar la unión por completo y hacer una memoria desdedouble
unauint64_t
variable diferente , luego hacer la prueba usando esa variable auxiliar.En x86-64 puede tener métodos extremadamente rápidos para verificar NaN e infinito, que funcionan independientemente de la
-ffast-math
opción del compilador. (f != f
,std::isnan
,std::isinf
Siempre producenfalse
con-ffast-math
).Las pruebas de NaN, infinito y números finitos se pueden hacer fácilmente al verificar el exponente máximo. infinito es máximo exponente con cero mantisa, NaN es máximo exponente y no cero mantisa. El exponente se almacena en los siguientes bits después del bit de signo superior, de modo que simplemente podemos desplazarnos a la izquierda para deshacernos del bit de signo y hacer que el exponente sea el bit superior, no
operator&
es necesario enmascarar ( ):Las
std
versiones deisinf
yisfinite
cargan 2double/float
constantes del.data
segmento y, en el peor de los casos, pueden causar 2 errores de caché de datos. Las versiones anteriores no cargan ningún dato,inf_double_shl1
y lasinf_float_shl1
constantes se codifican como operandos inmediatos en las instrucciones de ensamblaje.Más rápido
isnan2
son solo 2 instrucciones de montaje:Utiliza el hecho de que la
ucomisd
instrucción establece el indicador de paridad si algún argumento es NaN. Así es comostd::isnan
funciona cuando no-ffast-math
se especifica ninguna opción.fuente
El estándar IEEE dice que cuando el exponente es todo
1
sy la mantisa no es cero, el número es aNaN
. Doble es1
bit de signo,11
bits de exponente y52
bits de mantisa. Haz un poco de verificación.fuente
Como los comentarios anteriores indican a! = A no funcionará en g ++ y algunos otros compiladores, pero este truco debería funcionar. Puede que no sea tan eficiente, pero sigue siendo una forma:
Básicamente, en g ++ (aunque no estoy seguro de otros) printf imprime 'nan' en formatos% d o% .f si la variable no es un entero / flotante válido. Por lo tanto, este código verifica que el primer carácter de la cadena sea 'n' (como en "nan")
fuente
340282346638528859811704183484516925440.000
si a =FLT_MAX
. Tendría que usarchar s[7]; sprintf(s, "%.0g", a);
, que serán 6 canales sia=-FLT_MAX
, o-3e+38
Esto detecta el infinito y también NaN en Visual Studio comprobando que está dentro de los límites dobles:
fuente
FLT_MIN
,DBL_MIN
yLDBL_MIN
más cuidadosamente. Estos se definen como los valores normalizados más pequeños para cada tipo. Por ejemplo, la precisión simple tiene más de 8 millones de valores denorm legítimos que son mayores que cero y menores queFLT_MIN
(y no son NaN).