Domanda

C'è una funzione isNaN ()?

PS .: Sono in MinGW (se questo fa la differenza).

avevo questo risolto utilizzando isnan () da <math.h>, che non esiste in <cmath>, che ero #includeing all'inizio.

È stato utile?

Soluzione

Secondo lo standard IEEE, valori NaN hanno la proprietà strano che i confronti che coinvolgono loro sono sempre false. Cioè, per un galleggiante f, f != f sarà vero solo se f è NaN.

Si noti che, come alcuni commenti qui sotto hanno fatto notare, non tutti i compilatori rispettano questa quando l'ottimizzazione del codice.

Per qualsiasi compilatore che pretende di utilizzare IEEE in virgola mobile, questo trucco dovrebbe di lavoro. Ma non posso garantire che di lavoro nella pratica. Verificare con il proprio compilatore, in caso di dubbio.

Altri suggerimenti

Non c'è alcuna funzione isnan() disponibile in corrente C ++ Standard Library. E 'stato introdotto nel C99 e definito come un macro non una funzione. Elementi di libreria standard definiti da C99 non fanno parte della corrente di C ++ norma ISO / IEC 14882: 1998 né il suo aggiornamento ISO / IEC 14882:. 2003

Nel 2005 Technical Report 1 è stato proposto. Il TR1 porta compatibilità con C99 a C ++. Nonostante il fatto non è mai stato adottato ufficialmente a diventare standard di C ++, molti ( GCC 4.0+ o di Visual C ++ 9.0 o versione successiva C ++ implementazioni forniscono caratteristiche TR1 , tutti o solo alcuni (Visual C ++ 9.0 non fornisce funzioni matematiche C99).

Se TR1 è disponibile, allora cmath include elementi C99 come isnan(), isfinite(), ecc, ma essi sono definiti come funzioni, non le macro, di solito in std::tr1:: spazio dei nomi, anche se molte implementazioni (ad esempio GCC 4+ su Linux o in XCode su Mac OS X 10.5+) iniettare direttamente a std::, così std::isnan è ben definito.

Inoltre, alcune implementazioni di C ++ ancora fare C99 isnan() macro disponibile per C ++ (incluso attraverso cmath o math.h), ciò che può causare più confusione e gli sviluppatori possono assumere è un comportamento standard.

Una nota su Viusal C ++, come detto sopra, non fornisce std::isnanstd::tr1::isnan, ma fornisce una funzione di estensione definita come _isnan() che è disponibile da Visual C ++ 6.0

In XCode, v'è ancora più divertente. Come detto, GCC 4+ definisce std::isnan. Per le versioni precedenti di compilatore e modulo biblioteca XCode, a quanto pare (qui è rilevante discussione ), non hanno avuto la possibilità di controllare me) due funzioni sono definite, __inline_isnand() su Intel e __isnand() su Power PC.

Prima soluzione: se si utilizza C ++ 11

Dal momento che questo è stato chiesto c'erano un po 'di novità: è importante sapere che std::isnan() fa parte del C ++ 11

Sinossi

Definito in intestazione <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

determina se il dato punto floating numero arg è non-a-numero (NaN).

Parametri

arg: valore a virgola mobile

Valore restituito

true se arg è NaN, false altrimenti

Riferimento

http://en.cppreference.com/w/cpp/numeric/ math / isNaN

Si prega di notare che questo è incompatibile con -fast-math se si utilizza g ++, vedi sotto per altri suggerimenti.


Altre soluzioni: se si utilizza non C ++ 11 strumenti compatibili

Per C99, in C, questo è implementato come una macro isnan(c)that restituisce un valore int. Il tipo di x è float, double o long double.

I vari fornitori possono o non possono includere o meno una funzione isnan().

Il modo presumibilmente portatile per verificare NaN è quello di utilizzare la proprietà IEEE 754 che NaN non è uguale a se stesso. x == x cioè sarà false per x essere NaN

Tuttavia l'ultima opzione potrebbe non funzionare con ogni compilatore e alcune impostazioni (in particolare le impostazioni di ottimizzazione), quindi in ultima istanza, si può sempre controllare lo schema di bit ...

C'è anche un library header-solo presente in Boost che hanno gli strumenti puliti per affrontare floating point tipi di dati

#include <boost/math/special_functions/fpclassify.hpp>

È possibile ottenere le seguenti funzioni:

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);

Se avete tempo poi di dare un'occhiata a tutta la matematica toolkit da Boost, ha molti strumenti utili ed è in rapida crescita.

Anche quando si tratta di punti di galleggianti e non galleggianti potrebbe essere una buona idea di guardare il numerico Conversioni .

Ci sono tre modi "ufficiali": POSIX isnan macro , C ++ 0x modello di funzione isnan , o Visual C ++ funzione _isnan .

Purtroppo è piuttosto impraticabile per rilevare quale di quelli da utilizzare.

E, purtroppo, non c'è modo affidabile per stabilire se hai la rappresentazione IEEE 754 con NaN. La libreria standard offre un modo ufficiale (numeric_limits<double>::is_iec559). Ma in pratica, come compilatori g ++ vite che fino.

In teoria si potrebbe utilizzare semplicemente x != x , ma compilatori come g ++ e Visual C ++ vite che fino.

Così, alla fine, test per la specifica NaN bitpatterns , assumendo (e, si spera far rispettare, ad un certo punto!) Una particolare rappresentazione, come IEEE 754.


Modifica : come esempio di "compilatori quali g ++ ... vite che fino", in considerazione

#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 );
}

compilazione 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> _

C'è uno std :: isnan se compilatore supporta le estensioni C99, ma non sono sicuro se mingw fa.

Ecco una piccola funzione che dovrebbe funzionare se il compilatore non ha la funzione standard:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}

È possibile utilizzare numeric_limits<float>::quiet_NaN( ) definita nella libreria standard limits alla prova con. C'è una costante separata definita per 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;
}

Non so se questo funziona su tutte le piattaforme, come ho solo provato con g ++ su Linux.

È possibile utilizzare la funzione isnan(), ma è necessario includere la libreria C math.

#include <cmath>

Dato che questa funzione è parte di C99, non è disponibile ovunque. Se il fornitore non fornisce la funzione, è possibile anche definire il proprio variante per la compatibilità.

inline bool isnan(double x) {
    return x != x;
}

prevenzione nan

La mia risposta a questa domanda è non utilizzare i controlli retroattivi per nan . Utilizzare preventive i controlli per le divisioni della forma 0.0/0.0 , invece.

#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.

Risultati nan dal 0.f/0.f operazione, o 0.0/0.0. nan è una terribile nemesi per la stabilità del codice che deve essere rilevato e impedito con molta attenzione 1 . Le proprietà di nan che sono diversi dai numeri normali:

  • nan è tossico, (5 * nan = nan)
  • nan non è uguale a niente, nemmeno se stesso (nan! = nan)
  • nan non superiore a qualsiasi cosa (nan!> 0)
  • nan non è inferiore a nulla (nan! <0)

Gli ultimi 2 proprietà elencate sono contro-logica e si tradurrà in un comportamento strano di codice che si basa sul confronto con un numero nan (3 ° ultima proprietà è strano troppo, ma probabilmente non andando mai a vedere x != x ? nel codice (a meno che non si sta verificando per nan (inaffidabile))).

Nel mio codice, ho notato che i valori nan tendono a produrre difficile trovare bug. (Si noti come questo è non il caso per inf o -inf. (-inf <0) restituisce TRUE, (0 <inf) restituisce TRUE, e anche (-inf <inf) restituisce true. Quindi, a mio esperienza, il comportamento del codice è spesso ancora come desiderato).

cosa fare in nan

Che cosa si vuole che accada sotto 0.0/0.0 deve essere trattato come un caso speciale , ma ciò che si fa deve dipendere i numeri che ci si aspetta di venire fuori dal codice.

Nell'esempio precedente, il risultato di (0.f/FLT_MIN) verrà 0, sostanzialmente. Si consiglia 0.0/0.0 per generare HUGE invece. Così,

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.

Quindi, in quanto sopra, se x erano 0.f, inf si tradurrebbe (che ha abbastanza buon comportamento / non distruttivo come già detto in realtà).

Ricordate, divisione per 0 causa un'eccezione runtime . Quindi si deve sempre verificare la presenza di divisione intera per 0. Solo perché 0.0/0.0 valuta tranquillamente nan non significa che si può essere pigri e non verificare la presenza di 0.0/0.0 prima che accada.

1 Controlli per nan tramite x != x sono talvolta inaffidabili (x != x essendo spogliato fuori da alcuni compilatori ottimizzazione che rompono conformità IEEE, in particolare quando l'interruttore -ffast-math è attivato).

Il codice seguente utilizza la definizione di NAN (tutti i bit esponente impostati, almeno un po 'frazionale set) e si presuppone che sizeof (int) = sizeof (float) = 4. È possibile cercare NAN in Wikipedia per i dettagli.

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }

Al C ++ 14 ci sono un certo numero di modi per testare se un numero a virgola mobile value è NaN.

Di questi modi, solo verifica dei bit della rappresentazione del numero di, funziona in modo affidabile, come indicato nella mia risposta originale. In particolare, std::isnan e spesso proposto v != v controllo, non funzionano in modo affidabile e non deve essere utilizzato, per timore che il codice smette di funzionare correttamente quando qualcuno decide che è necessaria l'ottimizzazione in virgola mobile, e chiede al compilatore di farlo. Questa situazione può cambiare, compilatori possono ottenere più conforme, ma per questo problema che non è successo nei 6 anni da quando la risposta originale.

Per circa 6 anni mia risposta iniziale è stata la soluzione scelta per questa domanda, che era OK. Ma di recente è stata selezionata una risposta altamente upvoted raccomandando il test v != v inaffidabile. Quindi questo ulteriore più up-to-date risposta (ora abbiamo il C ++ 11 e 14 standard C ++ e C ++ 17 sull'orizzonte).


I modi principali per verificare la presenza di NaN-ness, come di C ++ 14, sono i seguenti:

  • std::isnan(value) )
    è il modo in libreria standard previsto dal C ++ 11. isnan apparentemente in conflitto con il Posix macro con lo stesso nome, ma in pratica, che non è un problema. Il problema principale è che quando virgola mobile ottimizzazione aritmetica è richiesto, quindi con almeno un compilatore principale, cioè g ++, std::isnan restituisce false all'argomento NaN .

  • (fpclassify(value) == FP_NAN) )
    Soffre dello stesso problema std::isnan, cioè, non è affidabile.

  • (value != value) )
    Consigliato in molte risposte SO. Soffre lo stesso problema come std::isnan, cioè, non è affidabile.

  • (value == Fp_info::quiet_NaN()) )
    Questa è una prova che, con comportamento standard non dovrebbe rilevare NaNs, ma che con la comportamento ottimizzato forse potrebbe rilevare NaNs (a causa di un codice ottimizzato proprietà dell'ufficio rappresentazioni bitlevel direttamente), e forse in combinazione con un altro modo di coprire il comportamento standard non-ottimizzato, potrebbe rilevare in modo affidabile NaN. purtroppo si è scoperto di non funzionare in modo affidabile.

  • (ilogb(value) == FP_ILOGBNAN) )
    Soffre dello stesso problema std::isnan, cioè, non è affidabile.

  • isunordered(1.2345, value) )
    Soffre dello stesso problema std::isnan, cioè, non è affidabile.

  • is_ieee754_nan( value ) )
    Questa non è una funzione standard. Si verifica dei bit secondo lo standard IEEE 754 standard. E 'completamente affidabile ma il codice è un po' dipendente dal sistema.


Nel seguente codice di prova “successo” è completa se un'espressione riporta Nan-ness del valore. Per la maggior parte le espressioni questa misura del successo, l'obiettivo di rilevare NaNs e solo NaNs, corrisponde alla loro semantica standard. Per l'espressione (value == Fp_info::quiet_NaN()) ), tuttavia, il comportamento standard è che non funziona come NaN-rivelatore.

#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 ) );
}

Risultati con g ++ (nota ancora una volta che il comportamento standard di (value == Fp_info::quiet_NaN()) è che non funziona come NaN-detector, è solo molto di interesse pratico qui):

[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)]
> _

Risultati 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)]
> _

Riassumendo i risultati di cui sopra, solo test diretti della rappresentazione a livello di bit, utilizzando la funzione is_ieee754_nan definita in questo programma di test, lavorato in modo affidabile in ogni caso sia con g ++ e Visual C ++.


Addendum:
Dopo aver postato il sopra mi sono reso conto di un altro possibile testare per NaN, di cui al un'altra risposta qui, vale a dire ((value < 0) == (value >= 0)). Che si è rivelato funzionare bene con Visual C ++, ma non riuscita con g ++ opzione 's -ffast-math. Solo test bitpattern diretta funziona in modo affidabile.

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);
}

Questo funziona se sizeof(int) è 4 e sizeof(long long) è 8.

Durante la fase di esecuzione è solo il confronto, fusioni non prendono in qualsiasi momento. Cambia solo la configurazione bandiere di confronto per verificare l'uguaglianza.

Una possibile soluzione che non dipende dalla rappresentazione IEEE specifico per NaN usato sarebbe il seguente:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}

Per quanto mi riguarda la soluzione potrebbe essere una macro per rendere più esplicito in linea e quindi abbastanza veloce. Funziona anche per qualsiasi tipo float. Si basa sul fatto che l'unico caso in cui un valore non è uguale a se stesso è quando il valore non è un numero.

#ifndef isnan
  #define isnan(a) (a != a)
#endif

Considerando che (! X = x) non è sempre garantita per NaN (ad esempio se si utilizza l'opzione -ffast-matematica), ho usato:

#define IS_NAN(x) (((x) < 0) == ((x) >= 0))

I numeri non possono essere sia <0 e> = 0, quindi davvero questo controllo passa solo se il numero è né inferiore, né superiore o uguale a zero. Che è fondamentalmente nessun numero a tutti, o NaN.

È inoltre possibile utilizzare questo, se si preferisce:

#define IS_NAN(x) (!((x)<0) && !((x)>=0)

Non sono sicuro di come questo è influenzato da -ffast-matematica, però, così la vostra situazione potrebbe essere diversa.

Questo funziona:

#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;
}

uscita: isNaN

Mi sembra che il miglior approccio realmente multipiattaforma sarebbe quella di utilizzare un sindacato e per testare il modello di bit del doppio per verificare la presenza di NaN.

Non ho accuratamente testato questa soluzione, e non ci può essere un modo più efficiente di lavorare con i modelli di bit, ma penso che dovrebbe funzionare.

#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;
}

Lo standard IEEE dice quando l'esponente è tutti 1s e la mantissa non è zero, il numero è un NaN. Doppio è 1 bit di segno, i bit esponente 11 e 52 mantissa bit. Fare un controllo po '.

In x86-64 si può avere metodi estremamente veloci per il controllo di Nan e l'infinito, che funzionano indipendentemente l'opzione -ffast-math compilatore. (f != f, std::isnan, std::isinf resa sempre false con -ffast-math).


Test per NaN, l'infinito e numeri finiti può essere facilmente fatta verificando per il massimo esponente. infinito è massimo esponente mantissa con zero NaN è massimo esponente e mantissa non zero. L'esponente viene memorizzato nei prossimi più in alto bit dopo bit di segno, in modo che possiamo solo spostamento a sinistra per sbarazzarsi del bit di segno e rendere il più alti esponenti dei bit, senza mascheramento (operator&) occorre:

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; }

Le versioni std di isinf e isfinite carico 2 costanti double/float dal segmento .data e nel peggiore dei casi possono causare 2 cache miss dati. Le versioni precedenti non vengono caricati tutti i dati, inf_double_shl1 e inf_float_shl1 costanti vengono codificati come operandi immediati nelle istruzioni di montaggio.


Faster isnan2 si trova a soli 2 istruzioni di montaggio:

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;
}

Utilizza il fatto che ucomisd istruzione imposta bandiera parità se uno degli argomenti è NaN. Questo è come funziona std::isnan quando viene specificata alcuna opzione -ffast-math.

Questo rileva l'infinito e anche NaN in Visual Studio controllando è all'interno di doppi limiti:

//#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;

Come osservazioni di cui sopra stato a! = A non funzioneranno in g ++ e alcuni altri compilatori, ma questo trucco dovrebbe. Esso non può essere il più efficiente, ma è ancora un modo:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

In sostanza, in g ++ (non sono sicuro di altri, però) stampe printf 'Nan' su% d o formati% .F se variabile non è un intero / float validi. Pertanto questo codice sta controllando per il primo carattere della stringa da 'n' (come in "nan")

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top