Ab C ++ 14 gibt es verschiedene Möglichkeiten, um zu testen, ob eine Gleitkommazahl value
eine NaN ist.
Von diesen Möglichkeiten funktioniert nur die Überprüfung der Bits der Zahlendarstellung zuverlässig, wie in meiner ursprünglichen Antwort angegeben. Insbesondere std::isnan
und die häufig vorgeschlagene Überprüfung v != v
funktionieren nicht zuverlässig und sollten nicht verwendet werden, damit Ihr Code nicht nicht mehr ordnungsgemäß funktioniert, wenn jemand entscheidet, dass eine Gleitkommaoptimierung erforderlich ist, und den Compiler dazu auffordert. Diese Situation kann sich ändern, Compiler können konformer werden, aber für dieses Problem ist dies in den 6 Jahren seit der ursprünglichen Antwort nicht aufgetreten.
Für ungefähr 6 Jahre war meine ursprüngliche Antwort die ausgewählte Lösung für diese Frage, die in Ordnung war. Vor kurzem wurde jedoch eine hoch gelobte Antwort ausgewählt, die den unzuverlässigen v != v
Test empfiehlt . Daher diese zusätzliche aktuellere Antwort (wir haben jetzt die Standards C ++ 11 und C ++ 14 sowie C ++ 17 am Horizont).
Die wichtigsten Möglichkeiten zur Überprüfung der NaN-Fähigkeit ab C ++ 14 sind:
std::isnan(value) )
ist die beabsichtigte Standardbibliothek seit C ++ 11. isnan
Anscheinend widerspricht es dem gleichnamigen Posix-Makro, aber in der Praxis ist das kein Problem. Das Hauptproblem besteht darin, dass, wenn eine Gleitkomma-Arithmetikoptimierung angefordert wird, mindestens ein Hauptcompiler, nämlich g ++, für das NaN-Argument std::isnan
zurückgibtfalse
.
(fpclassify(value) == FP_NAN) )
Leidet unter dem gleichen Problem std::isnan
, dh ist nicht zuverlässig.
(value != value) )
Empfohlen in vielen SO-Antworten. Leidet unter dem gleichen Problem std::isnan
, dh ist nicht zuverlässig.
(value == Fp_info::quiet_NaN()) )
Dies ist ein Test, der mit Standardverhalten keine NaNs erkennen sollte, der jedoch mit dem optimierten Verhalten möglicherweise NaNs erkennen könnte (aufgrund des optimierten Codes, der nur die Bitlevel-Darstellungen direkt vergleicht) und möglicherweise mit einer anderen Methode kombiniert wird, um das nicht optimierte Standardverhalten abzudecken konnte NaN zuverlässig nachweisen. Leider stellte sich heraus, dass es nicht zuverlässig funktionierte.
(ilogb(value) == FP_ILOGBNAN) )
Leidet unter dem gleichen Problem std::isnan
, dh ist nicht zuverlässig.
isunordered(1.2345, value) )
Leidet unter dem gleichen Problem std::isnan
, dh ist nicht zuverlässig.
is_ieee754_nan( value ) )
Dies ist keine Standardfunktion. Es überprüft die Bits gemäß dem IEEE 754-Standard. Es ist absolut zuverlässig, aber der Code ist etwas systemabhängig.
Im folgenden vollständigen Testcode ist „Erfolg“, ob ein Ausdruck die Nan-Ness des Werts meldet. Für die meisten Ausdrücke entspricht dieses Erfolgsmaß, das Ziel, NaNs und nur NaNs zu erkennen, ihrer Standardsemantik. Für den (value == Fp_info::quiet_NaN()) )
Ausdruck ist das Standardverhalten jedoch, dass er nicht als NaN-Detektor funktioniert.
#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 ) );
}
Ergebnisse mit g ++ (beachten Sie erneut, dass das Standardverhalten von (value == Fp_info::quiet_NaN())
ist, dass es nicht als NaN-Detektor funktioniert, es ist hier nur sehr von praktischem Interesse):
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> g ++ --version | finde "++"
g ++ (x86_64-win32-sjlj-rev1, erstellt vom MinGW-W64-Projekt) 6.3.0
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> g ++ foo.cpp && a
Der Compiler behauptet, IEEE 754 = true
v = nan, (std :: isnan (Wert)) = wahrer Erfolg
u = 3,14, (std :: isnan (Wert)) = false Erfolg
w = inf, (std :: isnan (Wert)) = false Erfolg
v = nan, ((fpclassify (value) == 0x0100)) = true Erfolg
u = 3,14, ((fpclassify (Wert) == 0x0100)) = false Erfolg
w = inf, ((fpclassify (value) == 0x0100)) = false Erfolg
v = nan, ((Wert! = Wert)) = wahrer Erfolg
u = 3,14, ((Wert! = Wert)) = falsch Erfolg
w = inf, ((Wert! = Wert)) = false Erfolg
v = nan, ((Wert == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((Wert == Fp_info :: quiet_NaN ())) = false Erfolg
w = inf, ((value == Fp_info :: quiet_NaN ())) = false Erfolg
v = nan, ((ilogb (Wert) == ((int) 0x80000000))) = wahr Erfolg
u = 3,14, ((ilogb (Wert) == ((int) 0x80000000))) = false Erfolg
w = inf, ((ilogb (Wert) == ((int) 0x80000000))) = false Erfolg
v = nan, (ungeordnet (1,2345, Wert)) = wahr Erfolg
u = 3,14, (ungeordnet (1,2345, Wert)) = falsch Erfolg
w = inf, (isunordered (1.2345, value)) = false Erfolg
v = nan, (is_ieee754_nan (Wert)) = true Erfolg
u = 3,14, (is_ieee754_nan (Wert)) = false Erfolg
w = inf, (is_ieee754_nan (Wert)) = false Erfolg
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> g ++ foo.cpp -ffast-math && a
Der Compiler behauptet, IEEE 754 = true
v = nan, (std :: isnan (Wert)) = false FAILED
u = 3,14, (std :: isnan (Wert)) = false Erfolg
w = inf, (std :: isnan (Wert)) = false Erfolg
v = nan, ((fpclassify (value) == 0x0100)) = false FAILED
u = 3,14, ((fpclassify (Wert) == 0x0100)) = false Erfolg
w = inf, ((fpclassify (value) == 0x0100)) = false Erfolg
v = nan, ((Wert! = Wert)) = false FAILED
u = 3,14, ((Wert! = Wert)) = falsch Erfolg
w = inf, ((Wert! = Wert)) = false Erfolg
v = nan, ((value == Fp_info :: quiet_NaN ())) = true Erfolg
u = 3.14, ((Wert == Fp_info :: quiet_NaN ())) = true FAILED
w = inf, ((value == Fp_info :: quiet_NaN ())) = true FAILED
v = nan, ((ilogb (Wert) == ((int) 0x80000000))) = wahr Erfolg
u = 3,14, ((ilogb (Wert) == ((int) 0x80000000))) = false Erfolg
w = inf, ((ilogb (Wert) == ((int) 0x80000000))) = false Erfolg
v = nan, (ungeordnet (1,2345, Wert)) = false FAILED
u = 3,14, (ungeordnet (1,2345, Wert)) = falsch Erfolg
w = inf, (isunordered (1.2345, value)) = false Erfolg
v = nan, (is_ieee754_nan (Wert)) = true Erfolg
u = 3,14, (is_ieee754_nan (Wert)) = false Erfolg
w = inf, (is_ieee754_nan (Wert)) = false Erfolg
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> _
Ergebnisse mit Visual C ++:
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> cl / nologo- 2> & 1 | finde "++"
Microsoft (R) C / C ++ - Optimierungscompiler Version 19.00.23725 für x86
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> cl foo.cpp / Feb && b
foo.cpp
Der Compiler behauptet, IEEE 754 = true
v = nan, (std :: isnan (Wert)) = wahrer Erfolg
u = 3,14, (std :: isnan (Wert)) = false Erfolg
w = inf, (std :: isnan (Wert)) = false Erfolg
v = nan, ((fpclassify (value) == 2)) = true Success
u = 3,14, ((fpclassify (Wert) == 2)) = false Erfolg
w = inf, ((fpclassify (value) == 2)) = false Erfolg
v = nan, ((Wert! = Wert)) = wahrer Erfolg
u = 3,14, ((Wert! = Wert)) = falsch Erfolg
w = inf, ((Wert! = Wert)) = false Erfolg
v = nan, ((Wert == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((Wert == Fp_info :: quiet_NaN ())) = false Erfolg
w = inf, ((value == Fp_info :: quiet_NaN ())) = false Erfolg
v = nan, ((ilogb (Wert) == 0x7fffffff)) = wahr Erfolg
u = 3,14, ((ilogb (Wert) == 0x7fffffff)) = false Erfolg
w = inf, ((ilogb (Wert) == 0x7fffffff)) = true FAILED
v = nan, (ungeordnet (1,2345, Wert)) = wahr Erfolg
u = 3,14, (ungeordnet (1,2345, Wert)) = falsch Erfolg
w = inf, (isunordered (1.2345, value)) = false Erfolg
v = nan, (is_ieee754_nan (Wert)) = true Erfolg
u = 3,14, (is_ieee754_nan (Wert)) = false Erfolg
w = inf, (is_ieee754_nan (Wert)) = false Erfolg
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> cl foo.cpp / Feb / fp: schnell && b
foo.cpp
Der Compiler behauptet, IEEE 754 = true
v = nan, (std :: isnan (Wert)) = wahrer Erfolg
u = 3,14, (std :: isnan (Wert)) = false Erfolg
w = inf, (std :: isnan (Wert)) = false Erfolg
v = nan, ((fpclassify (value) == 2)) = true Success
u = 3,14, ((fpclassify (Wert) == 2)) = false Erfolg
w = inf, ((fpclassify (value) == 2)) = false Erfolg
v = nan, ((Wert! = Wert)) = wahrer Erfolg
u = 3,14, ((Wert! = Wert)) = falsch Erfolg
w = inf, ((Wert! = Wert)) = false Erfolg
v = nan, ((Wert == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((Wert == Fp_info :: quiet_NaN ())) = false Erfolg
w = inf, ((value == Fp_info :: quiet_NaN ())) = false Erfolg
v = nan, ((ilogb (Wert) == 0x7fffffff)) = wahr Erfolg
u = 3,14, ((ilogb (Wert) == 0x7fffffff)) = false Erfolg
w = inf, ((ilogb (Wert) == 0x7fffffff)) = true FAILED
v = nan, (ungeordnet (1,2345, Wert)) = wahr Erfolg
u = 3,14, (ungeordnet (1,2345, Wert)) = falsch Erfolg
w = inf, (isunordered (1.2345, value)) = false Erfolg
v = nan, (is_ieee754_nan (Wert)) = true Erfolg
u = 3,14, (is_ieee754_nan (Wert)) = false Erfolg
w = inf, (is_ieee754_nan (Wert)) = false Erfolg
[C: \ my \ forums \ so \ 282 (NaN erkennen)]
> _
Zusammenfassend lässt sich sagen, dass nur das direkte Testen der Darstellung auf Bitebene mit der is_ieee754_nan
in diesem Testprogramm definierten Funktion in allen Fällen sowohl mit g ++ als auch mit Visual C ++ zuverlässig funktionierte.
Nachtrag:
Nachdem ich das oben Gesagte veröffentlicht hatte, wurde mir ein weiterer möglicher Test für NaN bewusst, der in einer anderen Antwort hier erwähnt wurde, nämlich ((value < 0) == (value >= 0))
. Das hat mit Visual C ++ gut funktioniert, ist aber mit der -ffast-math
Option von g ++ fehlgeschlagen . Nur direkte Bitmusterprüfungen funktionieren zuverlässig.