Frage

Gibt es eine isnan()-Funktion?

PS.:ich bin dabei MinGW (falls das einen Unterschied macht).

Ich habe dieses Problem mit isnan() von lösen lassen <math.h>, was in nicht existiert <cmath>, was ich war #includezunächst einmal.

War es hilfreich?

Lösung

Nach dem IEEE-Standard, haben NaN-Werte die ungeraden Eigenschaft, die Vergleiche die sie sind immer false. Das heißt, für einen Schwimmer f wird f != f wahr sein nur , wenn f NaN.

Beachten Sie, dass, wie einige Kommentare unten haben darauf hingewiesen, dass nicht alle Compiler dies zu respektieren, wenn der Code zu optimieren.

Für jeden Compiler, die IEEE-Gleitkomma verwenden behauptet, dieser Trick sollte Arbeit. Aber ich kann nicht garantieren, dass es wird in der Praxis funktionieren. Überprüfen Sie mit Ihrem Compiler, wenn im Zweifel.

Andere Tipps

Es gibt keine isnan() Funktion verfügbar in der aktuellen C ++ Standard Library. Es wurde in C99 und definiert als Makro keine Funktion. Die Elemente der Standardbibliothek definiert durch C99 sind nicht Teil der aktuellen C ++ Standard ISO / IEC 14882: 1998 weder die Aktualisierung der ISO / IEC 14882:. 2003

Im Jahr 2005 1 Technischer Bericht wurde vorgeschlagen. Die TR1 bringt Kompatibilität mit C99 zu C ++. Trotz der Tatsache, hat es nie offiziell angenommen worden C ++ Standard zu werden, viele ( GCC 4.0+ oder Visual C ++ 9.0+ C ++ Implementierungen TR1 liefern Funktionen , alle von ihnen oder nur einige (Visual C ++ 9.0 nicht C99 mathematische Funktionen zur Verfügung stellen).

Wenn TR1 verfügbar ist, dann schließt cmath C99 Elemente wie isnan(), isfinite() usw., aber sie sind als Funktionen definiert, keine Makros, in der Regel in std::tr1:: Namespace, obwohl viele Implementierungen (dh GCC 4+ auf Linux oder in XCode auf Mac OS X 10.5 und höher) injizieren sie direkt an std::, so std::isnan gut definiert ist.

Darüber hinaus einige Implementierungen von C ++ noch C99 isnan() Makro für C ++ zur Verfügung stellen (durch cmath oder math.h enthalten), was mehr Verwirrungen und Entwickler führen kann davon ausgehen können, es ist ein Standardverhalten.

Ein Hinweis zu Viusal C ++, wie oben erwähnt, es bietet keine std::isnan weder std::tr1::isnan, aber es bietet eine Erweiterungsfunktion als _isnan() definiert, die seit verfügbar ist Visual C ++ 6.0

Auf XCode, gibt es noch mehr Spaß. Wie bereits erwähnt, definiert GCC 4+ std::isnan. Bei älteren Versionen von Compiler und Bibliothek Form XCode scheint es (hier ist relevante Diskussion ), hat keine Gelegenheit gehabt, mich zu überprüfen) zwei Funktionen definiert sind, __inline_isnand() auf Intel und __isnand() auf Power PC.

Erste Lösung: Wenn Sie mit C ++ 11

Da dies wurde gebeten, ein wenig neuen Entwicklungen waren: Es ist wichtig, dass std::isnan() zu wissen Teil von C ++ 11

Überblick

Definiert in Header <cmath>

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

Bestimmt, ob die angegebene Gleitkommazahl arg ist not-a-Nummer (NaN).

Parameter

arg: Fließkommawert

Rückgabewert

true wenn arg NaN, false sonst

Referenz

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

Bitte beachten Sie, dass diese mit -fast-math unvereinbar ist, wenn Sie g ++ verwenden, siehe unten für weitere Vorschläge.


Andere Lösungen: Wenn Sie nicht C ++ 11-konforme Werkzeuge mit

Für C99, in C, wird dies als Makro implementiert isnan(c)that einen int-Wert zurückgibt. Die Art der x wird sein float, double oder long double.

Verschiedene Anbieter kann oder auch nicht eine Funktion isnan() enthalten oder nicht.

Der angeblich tragbare Weg für NaN zu überprüfen, ist die IEEE 754-Eigenschaft zu verwenden, die NaN nicht gleich selbst ist: d. H x == x für x seine NaN falsch sein

Doch die letzte Option mit jedem Compiler und einigen Einstellungen (insbesondere Optimierungseinstellungen) möglicherweise nicht, so in letzten Ausweg, können Sie immer das Bitmuster überprüfen ...

Es gibt auch eine numerische Konvertierungen .

Es gibt drei "offiziellen" Wege: Posix isnan Makro , c ++ 0x isnan Funktion Vorlage oder Visual C ++ _isnan Funktion .

Leider ist es eher unpraktisch zu erkennen, welche von denen, zu verwenden.

Und leider gibt es keine zuverlässige Möglichkeit zu erkennen, ob Sie IEEE 754 Darstellung mit NaNs haben. Die Standardbibliothek bietet eine offizielle solche Art und Weise (numeric_limits<double>::is_iec559). Aber in der Praxis Compiler wie g ++ Schraube das wieder wett.

In der Theorie könnte man einfach x != x , aber Compiler wie g ++ und Visual C ++, dass vermasseln.

Also am Ende, Test für die spezifischen NaN Bitmustern , unter der Annahme (und hoffentlich Durchsetzung irgendwann!) Eine bestimmte Darstellung wie IEEE 754.


Bearbeiten : als Beispiel für "Compiler wie g ++ ... Schraube dass up", berücksichtigt

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

Kompilieren mit 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> _

Es ist ein std :: isnan wenn Sie Compiler c99-Erweiterungen unterstützt, aber ich bin nicht sicher, ob mingw der Fall ist.

Hier ist eine kleine Funktion, die funktionieren sollte, wenn Ihr Compiler nicht die Standardfunktion hat:

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

Sie können numeric_limits<float>::quiet_NaN( ) in der limits Standardbibliothek definiert verwenden, um zu testen mit. Es gibt eine separate Konstante für double definiert.

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

Ich weiß nicht, ob dies auf allen Plattformen funktioniert, wie ich mit g ++ auf Linux nur getestet.

Sie können die isnan() Funktion verwenden, aber Sie müssen die C-Mathematik-Bibliothek aufzunehmen.

#include <cmath>

Da diese Funktion Teil des C99 ist, es ist nicht überall verfügbar. Falls der Hersteller die Funktion nicht liefern, können Sie auch Ihre eigene Variante für die Kompatibilität definieren.

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

nan Prävention

Meine Antwort auf diese Frage ist nicht rückwirkend überprüft nan verwenden . Mit präventiven prüft, ob Abteilungen der Form 0.0/0.0 statt.

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

nan Ergebnisse aus dem Betrieb 0.f/0.f oder 0.0/0.0. nan ist eine schreckliche Nemesis für die Stabilität des Codes, die erkannt werden müssen und verhindert sehr sorgfältig 1 . Die Eigenschaften von nan, die unterschiedlich von normalen Zahlen:

  • nan ist giftig, (5 * nan = nan)
  • ist nan zu irgendetwas nicht gleich, nicht einmal selbst (nan! = nan)
  • nan nicht größer als alles, was (nan!> 0)
  • nan ist nicht weniger als alles (nan! <0)

Die letzten 2 aufgeführten Eigenschaften sind Gegen logisch und wird in ungeraden Verhalten von Code führen, die auch auf Vergleiche verlässt sich seltsam mit einer nan Nummer (die dritte letzte Eigenschaft ist, aber sie sind wahrscheinlich nicht immer x != x ? im Code gehen, um zu sehen (es sei denn, Sie überprüfen für nan (unreliably))).

In meinem eigenen Code, bemerkte ich, dass nan Werte sind in der Regel schwer zu produzieren Fehler zu finden. (Beachten Sie, wie das ist nicht der Fall für inf oder -inf. (-inf <0) gibt TRUE, (0 <inf) gibt TRUE zurück, und sogar (-inf <inf) gibt TRUE zurück. Also, meiner Erfahrung ist das Verhalten des Codes oft noch wie gewünscht).

, was unter nan

tun

Was Sie unter 0.0/0.0 passieren soll, muss als Sonderfall behandelt werden, aber was Sie tun müssen, auf die Zahlen ab, die Sie aus dem Code kommen erwarten.

In dem obigen Beispiel wird das Ergebnis (0.f/FLT_MIN) 0, im Grunde werden. Vielleicht möchten Sie 0.0/0.0 HUGE statt zu erzeugen. Also,

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.

So in den oben genannten, wenn x 0.f sind, inf würde (was ziemlich gut / nicht-destruktives Verhalten hat wie oben tatsächlich erwähnt).

Denken Sie daran, integer Division durch 0 verursacht eine Laufzeitausnahme . So müssen Sie immer für Integer-Division durch 0 überprüfen Nur weil 0.0/0.0 wertet leise zu nan bedeutet nicht, Sie faul sein kann und nicht für 0.0/0.0 zu überprüfen, bevor es passiert.

1 Checks für nan über x != x manchmal unzuverlässig sind (x != x von einigen optimierende Compiler gezupft werden, die IEEE Compliance brechen, speziell wenn der -ffast-math Schalter aktiviert ist).

Der folgende Code verwendet die Definition von NAN (alle Exponentenbits gesetzt, mindestens ein Bruchbit gesetzt) ​​und geht davon aus, dass sizeof(int) = sizeof(float) = 4.Einzelheiten finden Sie in Wikipedia unter NAN.

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

Wie von C ++ 14 gibt es eine Reihe von Möglichkeiten zu testen, ob eine Gleitkommazahl value eine NaN ist.

Von diesen Möglichkeiten, nur Überprüfung der Bits der Vertretung der Zahl, arbeitet zuverlässig, in meiner ursprünglichen Antwort wie zur Kenntnis genommen. Insbesondere std::isnan und die oft vorgeschlagene Überprüfung v != v, funktionieren nicht zuverlässig und sollte nicht verwendet werden, damit Sie den Code richtig funktioniert nicht mehr, wenn jemand, dass die Optimierung Gleitkomma entscheidet benötigt wird, und fordert den Compiler zu tun. Diese Situation kann sich ändern, können Compiler anschmiegsamer, aber für dieses Problem, das in den letzten 6 Jahren, da die ursprüngliche Antwort nicht geschehen ist.

Für ca. 6 Jahre meine ursprüngliche Antwort war die gewählte Lösung für diese Frage, das in Ordnung war. Aber vor kurzem eine hoch upvoted Antwort den unzuverlässigen v != v Test empfohlen ausgewählt wurde. Daher ist diese zusätzlich mehr up-to-date Antwort (wir jetzt die C ++ 11 haben und C ++ 14-Standards und C ++ 17 am Horizont).


Die wichtigsten Möglichkeiten für NaN-ness, wie von C ++ 14 zu überprüfen, sind:

  • std::isnan(value) )
    ist die Standardbibliothek Weise dazu gedacht, da C ++ 11. isnan Konflikte offenbar mit der Posix-Makro mit dem gleichen Namen, aber in der Praxis ist das kein Problem ist. Das Hauptproblem ist, daß, wenn Punkt arithmetische Optimierungs floating angefordert wird, dann mit mindestens einem Haupt-Compiler, nämlich g ++, std::isnan liefert false für NaN Argument .

  • (fpclassify(value) == FP_NAN) )
    Leidet unter dem gleichen Problem wie std::isnan, das heißt, ist nicht zuverlässig.

  • (value != value) )
    Empfohlen in vielen SO Antworten. Leidet unter dem gleichen Problem wie std::isnan, d.h. ist nicht zuverlässig.

  • (value == Fp_info::quiet_NaN()) )
    Dies ist ein Test, dass mit Standardverhalten sollte NaNs nicht erkennen, aber das mit der vielleicht optimierte Verhalten könnte NaNs (durch optimierte Code erfassen nur den Vergleich bitlevel Darstellungen direkt) und vielleicht mit einer anderen Art und Weise zu kombinieren decken die Standard-un-optimierte Verhalten könnte zuverlässig erkennen NaN. Unglücklicherweise es stellte sich heraus, nicht zuverlässig.

  • (ilogb(value) == FP_ILOGBNAN) )
    Leidet unter dem gleichen Problem wie std::isnan, das heißt, ist nicht zuverlässig.

  • isunordered(1.2345, value) )
    Leidet unter dem gleichen Problem wie std::isnan, das heißt, ist nicht zuverlässig.

  • is_ieee754_nan( value ) )
    Dies ist keine Standardfunktion. Es ist die Überprüfung der Bits gemäß dem IEEE 754 Standard. Es ist absolut zuverlässig und der Code ist etwas systemabhängig.


In dem folgenden vollständigen Testcode „Erfolg“ ist, ob ein Ausdruck berichtet Nan-ness des Wertes. Für die meisten Ausdrücke dieses Maßstab für den Erfolg, ist das Ziel des Erfassen NaNs und nur NaNs, entspricht ihre Standardsemantik. Für den (value == Fp_info::quiet_NaN()) ) Ausdruck aber ist das Standardverhalten, dass es 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 ++ (Anmerkung erneut, dass das Standardverhalten von (value == Fp_info::quiet_NaN()) ist, dass es nicht als NaN-Detektor funktioniert, es ist nur sehr viel von praktischem Interesse hier):

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

Ergebnisse mit 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)]
> _

die obigen Ergebnisse zusammenfassend, nur direkte Prüfung der Bit-Ebene Darstellung, mit der is_ieee754_nan Funktion in diesem Testprogramm definiert, arbeitete zuverlässig in allen Fällen sowohl mit g ++ und Visual C ++.


Nachtrag:
Nach dem obigen Posting wurde ich von einer weiteren möglichen bewusst für NaN zu testen, unter eine andere Antwort hier, nämlich ((value < 0) == (value >= 0)). Das erwies sich mit Visual C ++ funktionieren aber nicht mit g ++ 's -ffast-math Option. Direkt Bitmuster Prüfung funktioniert zuverlässig.

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

Dies funktioniert, wenn sizeof(int) 4 und sizeof(long long) ist 8.

Während der Laufzeit ist es nur Vergleich, Castings übernehmen keine Zeit. Es ändert sich nur Vergleich Flags Konfiguration Gleichheit zu überprüfen.

Eine mögliche Lösung, die nicht auf der spezifischen IEEE-Darstellung für NaN abhängen würde verwendet, wäre die folgende:

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

Für mich könnte die Lösung ein Makro sein, es zu machen explizit inline und damit schnell genug. Es funktioniert auch für jeden Schwimmer-Typen. Es basiert auf der Tatsache, dass der einzigen Fall, wenn ein Wert nicht gleich selbst ist, wenn der Wert keine Zahl ist.

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

In Anbetracht, dass (! X = x) ist nicht immer für NaN garantiert (wie wenn die -ffast-Mathe-Option), ich habe mit:

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

Zahlen können nicht beide <0 und> = 0, so wirklich diese Prüfung passiert nur dann, wenn die Zahl weder weniger als noch größer oder gleich Null ist. Das ist im Grunde überhaupt keine Zahl oder NaN.

Sie können auch diese verwenden, wenn Sie bevorzugen:

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

Ich bin mir nicht sicher, wie dies durch -ffast-math obwohl betroffen, so die Leistung kann variieren.

Das funktioniert:

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

Ausgabe: isnan

Es scheint mir, dass der beste wirklich plattformübergreifende Ansatz, eine Gewerkschaft zu verwenden wäre und das Bitmuster des Doppels zu testen für NaNs zu überprüfen.

Ich habe nicht gründlich diese Lösung getestet, und es kann eine effizientere Art und Weise der Arbeit mit den Bitmustern, aber ich denke, dass es funktionieren soll.

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

Der IEEE-Standard sagt wenn der Exponent alle 1s und die Mantisse nicht Null ist, die Zahl ist ein NaN. Double ist 1 Vorzeichenbit, 11 Exponenten-Bits und 52 Mantissenbits. Haben ein bisschen zu überprüfen.

Auf x86-64 Sie extrem schnelle Methoden zur Kontrolle für NaN haben können und Unendlichkeit, die unabhängig von -ffast-math Compiler-Option arbeiten. (f != f, std::isnan, std::isinf immer false mit -ffast-math Ausbeute).


Test für NaN, Unendlichkeit und endliche Zahlen kann leicht für maximalen Exponenten durch Überprüfung erfolgen. Unendlichen maximale Exponent Mantisse mit null ist NaN maximale Exponent und Mantisse nicht Null. Der Exponent wird in den nächsten Bits nach dem obersten Vorzeichenbit gespeichert, so dass wir Verschiebung gerade verlassen können loszuwerden, das Vorzeichenbit zu bekommen und die Exponenten die obersten Bits machen, keine Maskierung (operator&) notwendig:

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

Die std Versionen von isinf und isfinite Last 2 double/float Konstanten aus .data Segment und im schlimmsten Fall können sie zwei Daten-Cache-Misses verursachen. Die oben genannten Versionen laden keine Daten, inf_double_shl1 und inf_float_shl1 Konstanten als unmittelbarer Operand in die Montageanleitung codiert erhalten.


Schneller isnan2 nur 2 Montageanleitung:

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

Verwendet die Tatsache, dass ucomisd Anweisung Parität Flag setzt, wenn ein Argument ist NaN. Dies ist, wie std::isnan funktioniert, wenn keine -ffast-math Optionen angegeben ist.

Dies erkennt Unendlichkeit und NaN auch in Visual Studio durch Überprüfung ist innerhalb von doppelten Grenzen:

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

Wie Kommentare über Zustand ein! = A nicht in g ++ arbeiten und einigen anderen Compiler, aber dieser Trick soll. Es kann nicht so effizient sein, aber es ist immer noch eine Möglichkeit:

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

Im Grunde genommen in g ++ (Ich bin über andere aber nicht sicher) printf druckt 'nan' auf% d oder% .f- Formate, wenn die Variable keine gültige integer / float ist. Daher ist dieser Code für das erste Zeichen der Zeichenfolge überprüft werden ‚n‘ (wie in „nan“)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top