A partire da C ++ 14 ci sono diversi modi per testare se un numero in virgola mobile valueè un NaN.
Di questi modi, solo il controllo dei bit della rappresentazione del numero funziona in modo affidabile, come indicato nella mia risposta originale. In particolare, std::isnane il controllo spesso proposto v != v, non funzionano in modo affidabile e non devono essere utilizzati, per evitare che il codice smetta di funzionare correttamente quando qualcuno decide che è necessaria l'ottimizzazione in virgola mobile e chiede al compilatore di farlo. Questa situazione può cambiare, i compilatori possono diventare più conformi, ma per questo problema che non si è verificato nei 6 anni dalla risposta originale.
Per circa 6 anni la mia risposta originale è stata la soluzione selezionata per questa domanda, che era OK. Ma recentemente v != vè stata selezionata una risposta altamente votata che raccomanda il test inaffidabile . Da qui questa ulteriore risposta più aggiornata (ora abbiamo gli standard C ++ 11 e C ++ 14 e C ++ 17 all'orizzonte).
I modi principali per verificare la presenza di NaN-ness, a partire da C ++ 14, sono:
std::isnan(value) )
è il modo di libreria standard previsto dal C ++ 11. isnanapparentemente è in conflitto con la macro Posix con lo stesso nome, ma in pratica non è un problema. Il problema principale è che quando viene richiesta l'ottimizzazione aritmetica in virgola mobile, con almeno un compilatore principale, vale a dire g ++, viene std::isnan restituito l' falseargomento NaN .
(fpclassify(value) == FP_NAN) )
Soffre dello stesso problema che std::isnan, ad esempio, non è affidabile.
(value != value) )
Consigliato in molte risposte SO. Soffre dello stesso problema che std::isnan, ad esempio, non è affidabile.
(value == Fp_info::quiet_NaN()) )
Questo è un test che con un comportamento standard non dovrebbe rilevare NaN, ma che con il comportamento ottimizzato potrebbe forse rilevare NaN (a causa del codice ottimizzato che confronta direttamente le rappresentazioni a livello di bit), e forse combinato con un altro modo per coprire il comportamento non ottimizzato standard , è in grado di rilevare in modo affidabile NaN. Purtroppo si è rivelato non funzionare in modo affidabile.
(ilogb(value) == FP_ILOGBNAN) )
Soffre dello stesso problema che std::isnan, ad esempio, non è affidabile.
isunordered(1.2345, value) )
Soffre dello stesso problema che std::isnan, ad esempio, non è affidabile.
is_ieee754_nan( value ) )
Questa non è una funzione standard. Sta verificando i bit secondo lo standard IEEE 754. È completamente affidabile ma il codice dipende in qualche modo dal sistema.
Nel seguente codice di test completo, "successo" è se un'espressione riporta nanosità del valore. Per la maggior parte delle espressioni questa misura di successo, l'obiettivo di rilevare NaN e solo NaN, corrisponde alla loro semantica standard. Per l' (value == Fp_info::quiet_NaN()) )espressione, tuttavia, il comportamento standard è che non funziona come un rivelatore 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 ) );
}
Risultati con g ++ (nota ancora che il comportamento standard di (value == Fp_info::quiet_NaN())è che non funziona come un rivelatore NaN, qui è solo molto di interesse pratico):
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> g ++ --version | trova "++"
g ++ (x86_64-win32-sjlj-rev1, costruito dal progetto MinGW-W64) 6.3.0
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> g ++ foo.cpp && a
Il compilatore afferma IEEE 754 = true
v = nan, (std :: isnan (value)) = true Successo
u = 3.14, (std :: isnan (value)) = false Operazione riuscita
w = inf, (std :: isnan (value)) = false Operazione riuscita
v = nan, ((fpclassify (value) == 0x0100)) = true Esito positivo
u = 3.14, ((fpclassify (value) == 0x0100)) = false Operazione riuscita
w = inf, ((fpclassify (value) == 0x0100)) = false Operazione riuscita
v = nan, ((value! = value)) = true Successo
u = 3.14, ((value! = value)) = false Operazione riuscita
w = inf, ((value! = value)) = false Operazione riuscita
v = nan, ((value == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = false Operazione riuscita
w = inf, ((value == Fp_info :: quiet_NaN ())) = false Operazione riuscita
v = nan, ((ilogb (value) == ((int) 0x80000000))) = true Successo
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = false Successo
w = inf, ((ilogb (value) == ((int) 0x80000000))) = false Successo
v = nan, (isunordered (1.2345, value)) = true Success
u = 3.14, (non ordinato (1.2345, valore)) = false Successo
w = inf, (isunordered (1.2345, value)) = false Operazione riuscita
v = nan, (is_ieee754_nan (value)) = true Successo
u = 3.14, (is_ieee754_nan (value)) = false Operazione riuscita
w = inf, (is_ieee754_nan (value)) = false Operazione riuscita
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> g ++ foo.cpp -ffast-math && a
Il compilatore afferma IEEE 754 = true
v = nan, (std :: isnan (value)) = false FAILED
u = 3.14, (std :: isnan (value)) = false Operazione riuscita
w = inf, (std :: isnan (value)) = false Operazione riuscita
v = nan, ((fpclassify (value) == 0x0100)) = false NON RIUSCITO
u = 3.14, ((fpclassify (value) == 0x0100)) = false Operazione riuscita
w = inf, ((fpclassify (value) == 0x0100)) = false Operazione riuscita
v = nan, ((value! = value)) = false FAILED
u = 3.14, ((value! = value)) = false Operazione riuscita
w = inf, ((value! = value)) = false Operazione riuscita
v = nan, ((value == Fp_info :: quiet_NaN ())) = true Successo
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 Successo
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = false Successo
w = inf, ((ilogb (value) == ((int) 0x80000000))) = false Successo
v = nan, (non ordinato (1.2345, valore)) = false NON RIUSCITO
u = 3.14, (non ordinato (1.2345, valore)) = false Successo
w = inf, (isunordered (1.2345, value)) = false Operazione riuscita
v = nan, (is_ieee754_nan (value)) = true Successo
u = 3.14, (is_ieee754_nan (value)) = false Operazione riuscita
w = inf, (is_ieee754_nan (value)) = false Operazione riuscita
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> _
Risultati con Visual C ++:
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> cl / nologo- 2> & 1 | trova "++"
Compilatore di ottimizzazione Microsoft (R) C / C ++ versione 19.00.23725 per x86
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> cl foo.cpp / Feb && b
foo.cpp
Il compilatore afferma IEEE 754 = true
v = nan, (std :: isnan (value)) = true Successo
u = 3.14, (std :: isnan (value)) = false Operazione riuscita
w = inf, (std :: isnan (value)) = false Operazione riuscita
v = nan, ((fpclassify (value) == 2)) = true Successo
u = 3.14, ((fpclassify (value) == 2)) = false Operazione riuscita
w = inf, ((fpclassify (value) == 2)) = false Operazione riuscita
v = nan, ((value! = value)) = true Successo
u = 3.14, ((value! = value)) = false Operazione riuscita
w = inf, ((value! = value)) = false Operazione riuscita
v = nan, ((value == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = false Operazione riuscita
w = inf, ((value == Fp_info :: quiet_NaN ())) = false Operazione riuscita
v = nan, ((ilogb (value) == 0x7fffffff)) = true Successo
u = 3.14, ((ilogb (value) == 0x7fffffff)) = false Successo
w = inf, ((ilogb (value) == 0x7fffffff)) = true FAILED
v = nan, (isunordered (1.2345, value)) = true Success
u = 3.14, (non ordinato (1.2345, valore)) = false Successo
w = inf, (isunordered (1.2345, value)) = false Operazione riuscita
v = nan, (is_ieee754_nan (value)) = true Successo
u = 3.14, (is_ieee754_nan (value)) = false Operazione riuscita
w = inf, (is_ieee754_nan (value)) = false Operazione riuscita
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> cl foo.cpp / feb / fp: veloce && b
foo.cpp
Il compilatore afferma IEEE 754 = true
v = nan, (std :: isnan (value)) = true Successo
u = 3.14, (std :: isnan (value)) = false Operazione riuscita
w = inf, (std :: isnan (value)) = false Operazione riuscita
v = nan, ((fpclassify (value) == 2)) = true Successo
u = 3.14, ((fpclassify (value) == 2)) = false Operazione riuscita
w = inf, ((fpclassify (value) == 2)) = false Operazione riuscita
v = nan, ((value! = value)) = true Successo
u = 3.14, ((value! = value)) = false Operazione riuscita
w = inf, ((value! = value)) = false Operazione riuscita
v = nan, ((value == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = false Operazione riuscita
w = inf, ((value == Fp_info :: quiet_NaN ())) = false Operazione riuscita
v = nan, ((ilogb (value) == 0x7fffffff)) = true Successo
u = 3.14, ((ilogb (value) == 0x7fffffff)) = false Successo
w = inf, ((ilogb (value) == 0x7fffffff)) = true FAILED
v = nan, (isunordered (1.2345, value)) = true Success
u = 3.14, (non ordinato (1.2345, valore)) = false Successo
w = inf, (isunordered (1.2345, value)) = false Operazione riuscita
v = nan, (is_ieee754_nan (value)) = true Successo
u = 3.14, (is_ieee754_nan (value)) = false Operazione riuscita
w = inf, (is_ieee754_nan (value)) = false Operazione riuscita
[C: \ my \ forum \ so \ 282 (rileva NaN)]
> _
Riassumendo i risultati di cui sopra, solo il test diretto della rappresentazione a livello di bit, utilizzando la is_ieee754_nanfunzione definita in questo programma di test, ha funzionato in modo affidabile in tutti i casi sia con g ++ che con Visual C ++.
Addendum:
Dopo aver pubblicato quanto sopra sono venuto a conoscenza di un altro possibile test per NaN, menzionato in un'altra risposta qui, vale a dire ((value < 0) == (value >= 0)). Ciò si è rivelato funzionare bene con Visual C ++ ma non è riuscito con l' -ffast-mathopzione di g ++ . Solo i test bitmap diretti funzionano in modo affidabile.