¿Hay una función isnan ()?
PD .: Estoy en MinGW (si eso hace la diferencia).
Esto se resolvió mediante el uso de isnan () de <math.h>
, que no existe en <cmath>
, que estaba #include
ing al principio.
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.
-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 uso x != 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), entonces x != 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 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::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 de cmath
o 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::isnan
ninguno 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.
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 :-)
Como se preguntó esto, hubo un poco de nuevos desarrollos: es importante saber que std::isnan()
es parte de C ++ 11
Definido en encabezado <cmath>
bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)
Determina si el número de coma flotante dado arg no es un número ( NaN
).
Parámetros
arg
: valor de coma flotante
Valor de retorno
true
si arg es NaN
, de lo false
contrario
Referencia
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.
Para C99, en C, esto se implementa como una macro isnan(c)
que devuelve un valor int. El tipo de x
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 que NaN
no es igual a sí misma, es decir x == x
, será falsa por x
ser 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 ...
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
y isnan
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á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-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
#include <boost/math/special_functions/fpclassify.hpp>
Obtiene las siguientes funciones:
template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);
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 .
Hay tres formas de "oficiales": POSIX isnan
macro , C ++ 0x isnan
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
#include <limits>
#include <assert.h>
void foo( double a, double b )
{
assert( a != b );
}
int main()
{
typedef std::numeric_limits<double> Info;
double const nan1 = Info::quiet_NaN();
double const 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> _
-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 uso x != 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 sea numeric_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ón numeric_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 una isnan
función, pero dado que GCC no se implementa correctamente is_iec559
ahora, supongo que tampoco lo hará en C ++ 0x, y -ffast-math
bien podría romperlo isnan
.
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)
{
volatile double d = var;
return d != d;
}
isnan
devuelva 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.
Puede usar numeric_limits<float>::quiet_NaN( )
definido en la limits
biblioteca estándar para probar. Hay una constante separada definida para double
.
#include <iostream>
#include <math.h>
#include <limits>
using namespace 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;
}
return 0;
}
No sé si esto funciona en todas las plataformas, ya que solo probé con g ++ en Linux.
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.
inline bool isnan(double x) {
return x != x;
}
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;
}
0x7fffffff
simplemente se quedaría en la memoria como ff ff ff 7f
. value
tiene 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.
0x7fff1234
También es un NaN. Así es0xffffffff
Mi respuesta a esta pregunta es no usar controles retroactivos paranan
. Utilice controles preventivos para las divisiones del formulario en su 0.0/0.0
lugar.
#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.
nan
resultados de la operación 0.f/0.f
, o 0.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 de nan
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 para inf
o -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).
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 desee 0.0/0.0
generar en su HUGE
lugar. Entonces,
float x=0.f, y=0.f, z;
if( !x && !y ) // 0.f/0.f case
z = FLT_MAX ; // biggest float possible
else
z = y/x ; // regular division.
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 silenciosamente nan
no significa que pueda ser vago y no verificar 0.0/0.0
antes de que suceda.
1 Las comprobaciones de nan
vía a x != 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).
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 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 != 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 el false
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 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.
#include <cmath> // std::isnan, std::fpclassify
#include <iostream>
#include <iomanip> // std::setw
#include <limits>
#include <limits.h> // CHAR_BIT
#include <sstream>
#include <stdint.h> // uint64_t
using namespace std;
#define TEST( x, expr, expected ) \
[&](){ \
const auto value = x; \
const bool result = expr; \
ostringstream stream; \
stream << boolalpha << #x " = " << x << ", (" #expr ") = " << result; \
cout \
<< setw( 60 ) << stream.str() << " " \
<< (result == expected? "Success" : "FAILED") \
<< endl; \
}()
#define TEST_ALL_VARIABLES( expression ) \
TEST( v, expression, true ); \
TEST( u, expression, false ); \
TEST( w, expression, false )
using Fp_info = numeric_limits<double>;
inline auto is_ieee754_nan( double const x )
-> bool
{
static constexpr bool is_claimed_ieee754 = Fp_info::is_iec559;
static constexpr int n_bits_per_byte = CHAR_BIT;
using Byte = unsigned char;
static_assert( is_claimed_ieee754, "!" );
static_assert( n_bits_per_byte == 8, "!" );
static_assert( sizeof( x ) == sizeof( uint64_t ), "!" );
#ifdef _MSC_VER
uint64_t const bits = reinterpret_cast<uint64_t const&>( x );
#else
Byte bytes[sizeof(x)];
memcpy( bytes, &x, sizeof( x ) );
uint64_t int_value;
memcpy( &int_value, bytes, sizeof( x ) );
uint64_t const& bits = int_value;
#endif
static constexpr uint64_t sign_mask = 0x8000000000000000;
static constexpr uint64_t exp_mask = 0x7FF0000000000000;
static constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF;
(void) sign_mask;
return (bits & exp_mask) == exp_mask and (bits & mantissa_mask) != 0;
}
auto main()
-> int
{
double const v = Fp_info::quiet_NaN();
double const u = 3.14;
double const w = Fp_info::infinity();
cout << boolalpha << left;
cout << "Compiler claims IEEE 754 = " << Fp_info::is_iec559 << endl;
cout << endl;;
TEST_ALL_VARIABLES( std::isnan(value) ); cout << endl;
TEST_ALL_VARIABLES( (fpclassify(value) == FP_NAN) ); cout << endl;
TEST_ALL_VARIABLES( (value != value) ); cout << endl;
TEST_ALL_VARIABLES( (value == Fp_info::quiet_NaN()) ); cout << endl;
TEST_ALL_VARIABLES( (ilogb(value) == FP_ILOGBNAN) ); cout << endl;
TEST_ALL_VARIABLES( isunordered(1.2345, value) ); cout << endl;
TEST_ALL_VARIABLES( is_ieee754_nan( value ) );
}
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_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.
inline bool IsNan(float f)
{
const uint32 u = *(uint32*)&f;
return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF); // Both NaN and qNan.
}
inline bool IsNan(double d)
{
const uint64 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.
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:
template<class T>
bool isnan( T f ) {
T _nan = (T)0.0/(T)0.0;
return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
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.
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 IEEE f != 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.
#ifndef isnan
#define isnan(a) (a != a)
#endif
Esto funciona:
#include <iostream>
#include <math.h>
using namespace std;
int main ()
{
char ch='a';
double val = nan(&ch);
if(isnan(val))
cout << "isnan" << endl;
return 0;
}
salida: isnan
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>
union NaN
{
uint64_t bits;
double num;
};
int main()
{
//Test if a double is NaN
double d = 0.0 / 0.0;
union NaN n;
n.num = d;
if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
{
printf("NaN: %f", d);
}
return 0;
}
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 desde double
una uint64_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 producen false
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 ( ):
static inline uint64_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;
}
static inline uint32_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;
}
constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_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.
static inline bool isnan2(double a) { return load_ieee754_rep(a) << 1 > inf_double_shl1; }
static inline bool isinf2(double a) { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1 < inf_double_shl1; }
static inline bool isnan2(float a) { return load_ieee754_rep(a) << 1 > inf_float_shl1; }
static inline bool isinf2(float a) { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a) { return load_ieee754_rep(a) << 1 < inf_float_shl1; }
Las std
versiones de isinf
yisfinite
cargan 2 double/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 las inf_float_shl1
constantes se codifican como operandos inmediatos en las instrucciones de ensamblaje.
Más rápido isnan2
son solo 2 instrucciones de montaje:
bool isnan2(double a) {
bool r;
asm(".intel_syntax noprefix"
"\n\t ucomisd %1, %1"
"\n\t setp %b0"
"\n\t .att_syntax prefix"
: "=g" (r)
: "x" (a)
: "cc"
);
return r;
}
Utiliza el hecho de que la ucomisd
instrucción establece el indicador de paridad si algún argumento es NaN. Así es como std::isnan
funciona cuando no -ffast-math
se especifica ninguna opción.
El estándar IEEE dice que cuando el exponente es todo 1
sy la mantisa no es cero, el número es a NaN
. Doble es 1
bit de signo, 11
bits de exponente y 52
bits 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:
bool IsNan(float a)
{
char s[4];
sprintf(s, "%.3f", a);
if (s[0]=='n') return true;
else return false;
}
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")
340282346638528859811704183484516925440.000
si 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
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;
FLT_MIN
, DBL_MIN
y LDBL_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 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).