Comprobación de si un doble (o float) es NaN en C ++
Pregunta
¿Hay una función isnan ()?
PS .: Estoy en MinGW (si eso hace la diferencia).
I había esta resuelto mediante el uso de isnan () desde <math.h>
, que no existe en <cmath>
, que estaba #include
ing al principio.
Solución
De acuerdo con el estándar IEEE, valores NaN tienen la propiedad extraño que las comparaciones que involucran ellos son siempre false. Es decir, por un flotador f, f != f
será cierto solamente si f es NaN.
Tenga en cuenta que, como algunos comentarios a continuación han señalado, no todos los compiladores respetan esta hora de optimizar el código.
Para cualquier compilador que pretende utilizar en coma flotante IEEE, este truco debe trabajo. Pero no puedo garantizar que trabajo en la práctica. Consulte con su compilador, en caso de duda.
Otros consejos
No hay ninguna función isnan()
disponible en la corriente C ++ biblioteca estándar. Se introdujo en C99 y se define como un macro no una función. Los elementos de biblioteca estándar definidos por C99 no son parte del C actual ++ estándar ISO / IEC 14882: 1998 ni su actualización ISO / IEC 14882:. 2003
En 2005 se propuso Informe Técnico 1. La TR1 trae compatibilidad con C99 a C ++. A pesar del hecho de que nunca se ha adoptado oficialmente para convertirse en C ++ estándar, muchos ( GCC 4.0 + o Visual C ++ 9.0+ C ++ implementaciones proporcionan características TR1 , todos ellos o sólo algunos (Visual C ++ 9.0 no proporciona funciones matemáticas C99).
Si TR1 está disponible, entonces cmath
C99 incluye elementos como isnan()
, isfinite()
, etc., sino que se definen como funciones, no macros, por lo general en espacio de nombres std::tr1::
, aunque muchas implementaciones (es decir CCG 4+ en Linux o en Mac en XCode OS X 10.5+) inyectarlos directamente a std::
, por lo std::isnan
está bien definido.
Por otra parte, algunas implementaciones de C ++ todavía hacen C99 isnan()
macro disponibles para C ++ (incluido a través cmath
o math.h
), lo que puede causar más confusiones y los desarrolladores pueden asumir que es un comportamiento estándar.
Una nota sobre Viusal C ++, como se mencionó anteriormente, no proporciona std::isnan
ni std::tr1::isnan
, sino que proporciona una función de extensión definida como _isnan()
que ha estado disponible desde Visual C ++ 6.0
En XCode, hay incluso más divertido. Como se ha mencionado, GCC 4+ define std::isnan
. Para versiones anteriores de compilador y la forma de la biblioteca XCode, parece (en este caso es discusión relevante), no han tenido la oportunidad de comprobar yo mismo) dos funciones están definidas, __inline_isnand()
en Intel y __isnand()
en Power PC.
Primera solución: si está utilizando C ++ 11
Dado que este se le preguntó que había un poco de nuevos desarrollos: es importante saber que std::isnan()
es parte de C ++ 11
Sinopsis
Definido en <cmath>
cabecera
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 arg dado es no-a-número (NaN
).
Parámetros
arg
: valor de coma flotante
Valor de retorno
true
si arg es NaN
, de lo contrario false
referencia
http://en.cppreference.com/w/cpp/numeric/ matemáticas / isnan
Tenga en cuenta que esto es incompatible con -Rápida-matemáticas si se utiliza g ++, ver más abajo para otras sugerencias.
Otras soluciones: si el uso de 11 herramientas que no cumplen C ++
Para C99, en C, esto se implementa como una macro isnan(c)
that devuelve un valor int. El tipo de x
será flotador, doble o doble largo.
Varios vendedores pueden o no pueden incluir o no un isnan()
función.
La forma supuestamente portátil para comprobar NaN
es utilizar la propiedad IEEE 754 que NaN
no es igual a sí mismo:. Es decir x == x
será falsa para x
ser NaN
Sin embargo, la última opción puede no funcionar con cada compilador y algunos parámetros (en particular, los ajustes de optimización), por lo que en última instancia, siempre se puede comprobar el patrón de bits ...
También hay un biblioteca sólo de encabezado presente en Boost que tienen herramientas claras para hacer frente a los tipos de datos de punto flotante
#include <boost/math/special_functions/fpclassify.hpp>
Usted 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 y luego echar un vistazo a todo conjunto de herramientas matemáticas de Boost, que tiene muchas herramientas útiles y está creciendo rápidamente.
También cuando se trata de puntos flotantes y no flotantes que podría ser una buena idea para buscar en el numérico Conversiones .
Hay tres maneras "oficiales": POSIX isnan
Función isnan
macro , C ++ 0x plantilla de función _isnan
, o Visual C ++.
Por desgracia, es muy poco práctico para detectar cuál de aquellos a utilizar.
Y, por desgracia, no hay manera confiable para detectar si usted tiene IEEE 754 de representación con NaNs. La biblioteca estándar ofrece una tal manera oficial (numeric_limits<double>::is_iec559
). Pero en los compiladores de práctica tales como g ++ tornillo que hacia arriba.
En teoría se podría usar simplemente x != x
, pero compiladores tales como g ++ y C visual ++ tornillo que hacia arriba.
Así que al final, prueba para el específico NaN bitpatterns , suponiendo (y es de esperar la aplicación de, en algún momento!) Una representación particular tal como IEEE 754.
editar : como un ejemplo de "compiladores tales como g ++ ... tornillo que arriba", considere
#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 );
}
Compilar con g ++ (TDM-2 mingw32) 4.4.1:
C:\test> type "C:\Program Files\@commands\gnuc.bat" @rem -finput-charset=windows-1252 @g++ -O -pedantic -std=c++98 -Wall -Wwrite-strings %* -Wno-long-long C:\test> gnuc x.cpp C:\test> a && echo works... || echo !failed works... C:\test> gnuc x.cpp --fast-math C:\test> a && echo works... || echo !failed Assertion failed: a != b, file x.cpp, line 6 This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. !failed C:\test> _
Hay un std :: isnan si compilador soporta extensiones de C99, pero no estoy seguro si lo hace MinGW.
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;
}
Puede utilizar numeric_limits<float>::quiet_NaN( )
definido en la biblioteca estándar limits
probar con. Hay una constante independiente definido 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 sólo probado con g ++ en Linux.
Puede utilizar la función isnan()
, pero hay que incluir la librería C matemáticas.
#include <cmath>
Como esta función es parte de C99, que no está disponible en todas partes. Si su proveedor no proporciona la función, también puede definir su propia variante de compatibilidad.
inline bool isnan(double x) {
return x != x;
}
prevención nan
Mi respuesta a esta pregunta es no utilice cheques retroactivos para nan
. Utilice preventivas los cheques para las divisiones de la forma 0.0/0.0
en su 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.
Resultados de nan
de la 0.f/0.f
operación, o 0.0/0.0
. nan
es una terrible némesis a la estabilidad de su código que debe detectarse y impedido con mucho cuidado 1 . Las propiedades de nan
que son diferentes de números normales:
-
nan
es tóxico, (5 *nan
=nan
) -
nan
no es igual a nada, ni siquiera en sí (nan
! =nan
) -
nan
no mayor que cualquier cosa (nan
!> 0) -
nan
no es inferior a nada (nan
! <0)
Las 2 últimas propiedades listadas son contra-lógica y dará lugar a un comportamiento extraño de código que se basa en la comparación con un número nan
(la tercera última propiedad es extraño también, pero es probable que no están nunca va a ver en el código x != x ?
(a menos que usted está mirando para nan (no fiable))).
En mi propio código, he notado que los valores tienden a producir nan
difícil de encontrar errores. (Tenga en cuenta cómo esto es no en el caso de inf
o -inf
. (-inf
<0) devuelve TRUE
, (0 <inf
) devuelve verdadero, e incluso (-inf
<inf
) devuelve verdadero. Por lo tanto, en mi experiencia, el comportamiento del código es a menudo sigue como se desee).
qué hacer bajo nan
Lo que usted quiere que suceda bajo 0.0/0.0
debe ser manejado como un caso especial , pero lo que haces tiene que depender de los números que se pueden esperar a salir 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
para generar HUGE
lugar. Por lo tanto,
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.
Así que en el anterior, si x eran 0.f
, inf
daría lugar (que tiene muy buen comportamiento / no destructivos como se mencionó anteriormente en realidad).
Recuerde, la división por 0 produce una excepción de ejecución . Por lo que siempre debe comprobar si hay división entera por 0. El hecho de que 0.0/0.0
evalúa en voz baja a nan
no significa que usted puede ser perezoso y no comprueba 0.0/0.0
antes de que suceda.
1 Cheques para nan
vía x != x
son a veces poco fiables (x != x
ser despojado a cabo por algunos compiladores que optimizan que rompen el cumplimiento IEEE, específicamente cuando está activado el interruptor -ffast-math
).
El siguiente código utiliza la definición de NAN (todos los bits de exponente establecidos, por lo menos un conjunto de bits fraccional) y se supone que sizeof (int) = sizeof (float) = 4. Se puede consultar en Wikipedia NAN para los detalles.
bool IsNan( float value )
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
A partir de C ++ 14 hay una serie de maneras de probar si un número de punto value
flotante es un NaN.
De esta manera, solamente comprobación de los bits de la representación del número,
funciona de forma fiable, como se ha señalado en mi respuesta original. En particular, std::isnan
y el cheque v != v
propone a menudo, no funcionan de forma fiable y no debe ser utilizado, no sea que su código deja de funcionar correctamente cuando alguien decide que es necesaria la optimización de punto flotante, y pide al compilador para hacer eso. Esta situación puede cambiar, compiladores puede obtener más conformes, pero para este problema que no ha ocurrido en los últimos 6 años desde que la respuesta original.
Para unos 6 años mi primera respuesta fue la solución seleccionada para esta pregunta, que estaba bien. Sin embargo, recientemente una respuesta altamente upvoted recomendar la prueba v != v
poco fiable ha sido seleccionado. De ahí que este adicional más hasta a la fecha de respuesta (que ahora tiene el C ++ y C ++ 11 14 normas, y C ++ 17 en el horizonte).
Las principales formas de comprobar NaN-dad, a partir de C ++ 14, son:
-
std::isnan(value) )
es la forma biblioteca estándar destinado desde C ++ 11.isnan
aparentemente en conflicto con el POSIX macro del mismo nombre, pero en la práctica eso no es un problema. El principal problema es que cuando flotante punto optimización aritmética se solicita, a continuación, con al menos un compilador principal, es decir, g ++,std::isnan
devuelvefalse
para el argumento NaN . -
(fpclassify(value) == FP_NAN) )
Sufre del mismo problema questd::isnan
, es decir, no es fiable. -
(value != value) )
Se recomienda en muchas respuestas SO. Adolece del mismo problema questd::isnan
, es decir, no es fiable. -
(value == Fp_info::quiet_NaN()) )
Esta es una prueba que con el comportamiento estándar no debe detectar NaNs, pero que con la comportamiento optimizado quizá podría detectar NaNs (debido al código optimizado simplemente comparando el representaciones bitlevel directamente), y tal vez combinar con otra forma de cubrir el comportamiento estándar optimizado-un, podría detectar de forma fiable NaN. Desafortunadamente resultó que no funcione correctamente. -
(ilogb(value) == FP_ILOGBNAN) )
Sufre del mismo problema questd::isnan
, es decir, no es fiable. -
isunordered(1.2345, value) )
Sufre del mismo problema questd::isnan
, es decir, no es fiable. -
is_ieee754_nan( value ) )
Esta no es una función estándar. Es la comprobación de los bits de acuerdo con la norma IEEE 754 estándar. Es completamente fiable y el código es algo dependiente del sistema.
En el siguiente código de prueba “éxito” es completa si una expresión informa Nan-dad del valor. Para la mayoría de las expresiones esta medida de éxito, el objetivo de detectar y NaNs única NaNs, se corresponde con su semántica estándar. Para la expresión (value == Fp_info::quiet_NaN()) )
, sin embargo, 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 ) );
}
Los resultados con g ++ (nota una vez más que el comportamiento estándar de (value == Fp_info::quiet_NaN())
es que no funciona como un detector de NaN, es sólo mucho interés práctico aquí):
[C:\my\forums\so\282 (detect NaN)] > g++ --version | find "++" g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0 [C:\my\forums\so\282 (detect NaN)] > g++ foo.cpp && a Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 0x0100)) = true Success u = 3.14, ((fpclassify(value) == 0x0100)) = false Success w = inf, ((fpclassify(value) == 0x0100)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == ((int)0x80000000))) = true Success u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false Success w = inf, ((ilogb(value) == ((int)0x80000000))) = false Success v = nan, (isunordered(1.2345, value)) = true Success u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > g++ foo.cpp -ffast-math && a Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = false FAILED u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 0x0100)) = false FAILED u = 3.14, ((fpclassify(value) == 0x0100)) = false Success w = inf, ((fpclassify(value) == 0x0100)) = false Success v = nan, ((value != value)) = false FAILED u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = true Success u = 3.14, ((value == Fp_info::quiet_NaN())) = true FAILED w = inf, ((value == Fp_info::quiet_NaN())) = true FAILED v = nan, ((ilogb(value) == ((int)0x80000000))) = true Success u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false Success w = inf, ((ilogb(value) == ((int)0x80000000))) = false Success v = nan, (isunordered(1.2345, value)) = false FAILED u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > _
Los resultados con Visual C ++:
[C:\my\forums\so\282 (detect NaN)] > cl /nologo- 2>&1 | find "++" Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86 [C:\my\forums\so\282 (detect NaN)] > cl foo.cpp /Feb && b foo.cpp Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 2)) = true Success u = 3.14, ((fpclassify(value) == 2)) = false Success w = inf, ((fpclassify(value) == 2)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == 0x7fffffff)) = true Success u = 3.14, ((ilogb(value) == 0x7fffffff)) = false Success w = inf, ((ilogb(value) == 0x7fffffff)) = true FAILED v = nan, (isunordered(1.2345, value)) = true Success u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > cl foo.cpp /Feb /fp:fast && b foo.cpp Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 2)) = true Success u = 3.14, ((fpclassify(value) == 2)) = false Success w = inf, ((fpclassify(value) == 2)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == 0x7fffffff)) = true Success u = 3.14, ((ilogb(value) == 0x7fffffff)) = false Success w = inf, ((ilogb(value) == 0x7fffffff)) = true FAILED v = nan, (isunordered(1.2345, value)) = true Success u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > _
Resumiendo los resultados anteriores, sólo se prueba directa de la representación a nivel de bits, utilizando la función is_ieee754_nan
definido en este programa de prueba, trabajó de forma fiable en todos los casos, tanto con g ++ y Visual C ++.
Adición:
Después de la publicación de la anterior me di cuenta de otra posible para la prueba de NaN, se menciona en otra respuesta aquí, a saber ((value < 0) == (value >= 0))
. Eso resultó funcionar bien con Visual C ++, pero fallaron con g ++ opción -ffast-math
's. Sólo las pruebas bitpattern directa funciona de forma fiable.
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 única comparación, piezas de fundición no toman cualquier momento. Sólo cambia la configuración banderas de comparación para comprobar la igualdad.
Una posible solución que no dependería de la representación IEEE específico para NaN utilizado sería el 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) );
}
En cuanto a mí la solución podría ser una macro para que sea de forma explícita 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 en el que 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
Teniendo en cuenta que (! X = x) no siempre está garantizada por NaN (como por ejemplo si se utiliza la opción -ffast-matemáticas), 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 sólo pasa si el número no es ni menos de, ni mayor que o igual a cero. Que es básicamente ningún número, o NaN.
También es posible usar esto si lo prefiere:
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
No estoy seguro de cómo esto se ve afectada por -ffast-matemáticas, sin embargo, por lo que su experiencia puede variar.
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 el uso de un sindicato y para probar el patrón de bits de la doble para comprobar si hay NaNs.
No he probado a fondo esta solución, y puede haber una manera 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;
}
El estándar IEEE dice
cuando el exponente es todo 1
s
y
la mantisa no es cero,
el número es un NaN
.
Doble es el bit de signo 1
, bits de exponente 11
y los bits de mantisa 52
.
Hacer una verificación de bits.
En x86-64 puede tener métodos extremadamente rápidos para la comprobación de NaN y el infinito, que funcionan independientemente opción -ffast-math
compilador de. (f != f
, std::isnan
, std::isinf
siempre dió false
con -ffast-math
).
Las pruebas de NaN, infinito y más números finitos se puede hacer fácilmente mediante la comprobación de máximo exponente. 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 más elevado, por lo que sólo podemos desplazamiento a la izquierda para deshacerse del bit de signo y hacer que el exponente de los bits más altas, sin enmascaramiento (operator&
) es necesario:
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 versiones std
de isinf
y isfinite
carga 2 constantes double/float
de segmento .data
y en el peor de los casos que pueden causar 2 fallos de caché de datos. Las versiones anteriores no se cargan los datos, constantes inf_double_shl1
y inf_float_shl1
consiguen codifican como operandos inmediatos en las instrucciones de montaje.
isnan2
más rápido está a sólo 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 ucomisd
instrucción establece la bandera de paridad si cualquier argumento es NaN. Así es como funciona std::isnan
cuando no se especifica opciones -ffast-math
.
Este detecta el infinito y NaN en Visual Studio mediante la comprobación de 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;
A medida que los comentarios de arriba del estado a! = A no funcionarán en g ++ y algunos otros compiladores, pero este truco debería. Puede que no sea tan eficiente, pero aún así es una manera:
bool IsNan(float a)
{
char s[4];
sprintf(s, "%.3f", a);
if (s[0]=='n') return true;
else return false;
}
Básicamente, en g ++ (no estoy seguro acerca de los demás, aunque las impresiones) printf 'nan' sobre los formatos% .F% d o si la variable no es un número entero / flotante válido. Por lo tanto, este código está comprobando para el primer carácter de la cadena a 'n' (como en "nan")