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 #includeing al principio.

¿Fue útil?

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 devuelve false para el argumento NaN .

  • (fpclassify(value) == FP_NAN) )
    Sufre del mismo problema que std::isnan, es decir, no es fiable.

  • (value != value) )
    Se recomienda en muchas respuestas SO. Adolece del mismo problema que std::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 que std::isnan, es decir, no es fiable.

  • isunordered(1.2345, value) )
    Sufre del mismo problema que std::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 1s 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")

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top