Überprüfen, ob ein Double (oder Float) in C ++ NaN ist


368

Gibt es eine isnan () Funktion?

PS.: Ich bin in MinGW (wenn das einen Unterschied macht).

Ich hatte dies gelöst, indem ich isnan () von verwendete <math.h>, das nicht existiert, in <cmath>dem ich #includezuerst war.


2
Ich bin nicht rein, du kannst es portabel machen. Wer sagt, dass C ++ IEEE754 erfordert?
David Heffernan


Nur eine Anmerkung, 1 Unze Vorbeugung ist besser als 1 Pfund Heilung. Mit anderen Worten, es ist weitaus besser zu verhindern , dass 0.f / 0.f jemals ausgeführt wird, als rückwirkend nach nan's in Ihrem Code zu suchen . nanDies kann für Ihr Programm furchtbar zerstörerisch sein. Wenn es sich vermehren darf, kann es schwer zu findende Fehler verursachen. Dies liegt daran, dass nanes giftig ist (5 * nan= nan), nannichts gleich ist ( nan! = nan), nanNicht größer als alles ( nan!> 0), nannicht kleiner als alles ( nan! <0) ist.
Bobobobo

1
@bobobobo: Dies ist eine Funktion, die eine zentralisierte Fehlerprüfung ermöglicht. Genau wie Ausnahmen gegen Rückgabewerte.
Ben Voigt

2
Warum hat <cmath> kein isnan ()? Es ist in std ::
frankliuao

Antworten:


349

Gemäß dem IEEE-Standard haben NaN-Werte die ungerade Eigenschaft, dass Vergleiche, an denen sie beteiligt sind, immer falsch sind. Das heißt, für einen Float f ist nur dann f != fwahr , wenn f NaN ist.

Beachten Sie, dass, wie einige Kommentare unten gezeigt haben, dies nicht alle Compiler bei der Optimierung von Code berücksichtigen.

Für jeden Compiler, die IEEE - Gleitkomma verwenden behauptet, dieser Trick sollte funktionieren. Aber ich kann nicht garantieren , dass es wird in der Praxis funktionieren. Fragen Sie im Zweifelsfall Ihren Compiler.


4
Der Compiler sollte dies besser nicht entfernen, wenn er in einem IEEE-Modus ausgeführt wird. Überprüfen Sie die Dokumentation für Ihren Compiler natürlich ...
dmckee --- Ex-Moderator Kätzchen

38
-1 funktioniert nur in der Theorie, nicht in der Praxis: Compiler wie g ++ (mit -fastmath) vermasseln das. Der einzige allgemeine Weg, bis c ++ 0x, besteht darin, auf Bitmuster zu testen.
Prost und hth. - Alf

66
@Alf: Die Dokumentation für die -ffast-mathOption besagt ausdrücklich, dass es zu einer falschen Ausgabe für Programme kommen kann, die von einer genauen Implementierung abhängen, wenn IEEE- oder ISO-Regeln / Spezifikationen für mathematische Funktionen vorliegen. Ohne diese Option ist die Verwendung x != xeine absolut gültige und tragbare Methode zum Testen von NaN.
Adam Rosenfield

7
@Adam: In der Dokumentation wird offen angegeben, dass sie nicht konform ist, ja. und ja, ich bin diesem Argument schon einmal begegnet und habe es ausführlich mit Gabriel Dos Reis besprochen. Es wird häufig verwendet, um das Design in einem Zirkelargument zu verteidigen (ich weiß nicht, ob Sie damit in Verbindung bringen wollten, aber es lohnt sich zu wissen - es ist Flammenkriegsmaterial). Ihre Schlussfolgerung, x != xdie ohne diese Option gültig ist, folgt nicht logisch. Dies kann für eine bestimmte Version von g ++ zutreffen oder nicht. Auf jeden Fall können Sie im Allgemeinen nicht garantieren, dass die Fastmath-Option nicht verwendet wird.
Prost und hth. - Alf

7
@Alf: Nein, ich war mir Ihrer Diskussion mit Gabriel Dos Reis nicht bewusst. Steve Jessop machte einen großen Punkt in der anderen Frage zur Annahme einer IEEE-Vertretung. Wenn Sie von IEEE 754 ausgehen und davon ausgehen, dass der Compiler konform arbeitet (dh ohne die -ffast-mathOption), x != xhandelt es sich um eine gültige und tragbare Lösung. Sie können sogar testen, -ffast-mathindem Sie das __FAST_MATH__Makro testen und in diesem Fall zu einer anderen Implementierung wechseln (z. B. Gewerkschaften und Bit-Twiddling verwenden).
Adam Rosenfield

220

isnan()In der aktuellen C ++ - Standardbibliothek ist keine Funktion verfügbar. Es wurde in C99 eingeführt und als Makro und nicht als Funktion definiert. Von C99 definierte Elemente der Standardbibliothek sind weder Teil des aktuellen C ++ - Standards ISO / IEC 14882: 1998 noch dessen Aktualisierung ISO / IEC 14882: 2003.

Im Jahr 2005 wurde der technische Bericht 1 vorgeschlagen. Der TR1 bringt Kompatibilität mit C99 zu C ++. Trotz der Tatsache, dass es nie offiziell als C ++ - Standard übernommen wurde, bieten viele ( GCC 4.0+ oder Visual C ++ 9.0+ C ++ - Implementierungen TR1-Funktionen, alle oder nur einige (Visual C ++ 9.0 bietet keine C99-Mathematikfunktionen). .

Wenn TR1 verfügbar ist, dann cmathschließt 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) inject sie direkt zu std::, std::isnanist also gut definiert.

Darüber hinaus stellen einige Implementierungen von C ++ das C99- isnan()Makro weiterhin für C ++ zur Verfügung (enthalten durch cmathoder math.h), was zu weiteren Verwirrungen führen kann und Entwickler möglicherweise davon ausgehen, dass es sich um ein Standardverhalten handelt.

Ein Hinweis zu Viusal C ++, wie oben erwähnt, bietet std::isnanweder eine std::tr1::isnan, noch eine Erweiterungsfunktion, _isnan()die seit Visual C ++ 6.0 verfügbar ist

Auf XCode macht es noch mehr Spaß. Wie bereits erwähnt, definiert GCC 4+ std::isnan. Für ältere Versionen des Compilers und der Bibliothek von XCode scheinen (hier ist eine relevante Diskussion ), ich hatte keine Gelegenheit, mich selbst zu überprüfen) zwei Funktionen definiert worden, __inline_isnand()auf Intel und __isnand()auf Power PC.


21
Jeder möchte diese Funktionen wie isNan oder isInfinity. Warum nehmen die Verantwortlichen nicht einfach in ihre Standards auf? - Ich werde versuchen, herauszufinden, wie ich die Verantwortung übernehmen kann, und meine Stimme dafür abgeben. Ernsthaft.
Shuhalo

8
@shuhalo Schon verantwortlich?
Tomáš Zato - Wiedereinsetzung Monica

11
Diese Antwort sollte aktualisiert werden, da sie std::isnanjetzt Teil des C ++ 11-Standards ist und sich die Unterstützung ausgebreitet hat. std :: isnan wurde in Visual Studio ab Visual Studio 2013 implementiert. Vielleicht wurde @shuhalo verantwortlich :-)
aberaud

170

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

Da dies gefragt wurde, gab es einige neue Entwicklungen: Es ist wichtig zu wissen, dass dies std::isnan()Teil von C ++ 11 ist

Zusammenfassung

In der Kopfzeile definiert <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 keine Zahl ist ( NaN).

Parameter

arg: Gleitkommawert

Rückgabewert

truewenn arg ist NaN, falsesonst

Referenz

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

Bitte beachten Sie, dass dies nicht mit -fast-math kompatibel ist, wenn Sie g ++ verwenden. Weitere Vorschläge finden Sie weiter unten.


Andere Lösungen: Wenn Sie nicht C ++ 11-kompatible Tools verwenden

Für C99 wird dies in C als Makro implementiert isnan(c), das einen int-Wert zurückgibt. Die Art xmuss schwimmend, doppelt oder lang doppelt sein.

Verschiedene Anbieter können eine Funktion enthalten oder nicht isnan().

Die vermeintlich tragbare Methode zur Überprüfung NaNbesteht darin, die IEEE 754-Eigenschaft zu verwenden, NaNdie nicht gleich sich selbst ist: dh x == xfür das xSein falsch ist NaN.

Die letzte Option funktioniert jedoch möglicherweise nicht mit jedem Compiler und einigen Einstellungen (insbesondere Optimierungseinstellungen), sodass Sie als letztes Mittel immer das Bitmuster überprüfen können ...


8
Verdient definitiv die akzeptierte Antwort und verdient mehr positive Stimmen. Danke für den Tipp
LBes

3
−1 std::isnan ist ab Februar 2017 immer noch eine schlechte Empfehlung, da es mit der Gleitkommaoptimierung von g ++ nicht funktioniert.
Prost und hth. - Alf

@ Cheersandhth.-Alf: Ist diese Option IEEE-konform? Die Antwort wurde bearbeitet
BlueTrin

@BlueTrin: Beide x != xund isnansind erforderlich, um die IEEE 754-Konformität zu gewährleisten . In Bezug auf letzteres besagt der IEEE 754-2008-Standard, dass „Implementierungen die folgenden nicht rechnerischen Operationen für alle unterstützten arithmetischen Formate bereitstellen müssen“ und „isNaN (x) genau dann wahr ist, wenn x ein NaN ist“. Zur Überprüfung der Konformität, die dieser Standard erfordert, is754version1985()und is754version2008()wo C ++ stattdessen bietet std::numeric_limits<Fp>::is_iec559()(IEC 559 ist der gleiche Standard). Leider -ffast-mathbeansprucht g ++ bei der Optimierung die Konformität, ist aber nicht konform.
Prost und hth. - Alf

1
Warnung: isnan (x) funktioniert nicht mit der Option -ffinite-math-only in gcc und clang
A Fog

82

In Boost gibt es auch eine Bibliothek nur für Header , die über nützliche Tools für den Umgang mit Gleitkomma-Datentypen verfügt

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

Sie erhalten folgende Funktionen:

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

Wenn Sie Zeit haben, schauen Sie sich das gesamte Math-Toolkit von Boost an. Es enthält viele nützliche Tools und wächst schnell.

Auch beim Umgang mit Gleitkomma- und Nicht-Gleitkommawerten ist es möglicherweise eine gute Idee, sich die numerischen Konvertierungen anzusehen .


1
Vielen Dank! Genau das, wonach ich gesucht habe.
Dr. Watson

Es wurde in Boost 1.35 hinzugefügt (ich habe gerade festgestellt, dass mein Programm unter der alten Linux-Distribution nicht kompiliert werden kann).
Marcin

2
Wenn Sie mit der Option --fast-math kompilieren, funktioniert diese Funktion nicht wie erwartet.
Gaetano Mendola

43

Es gibt drei "offizielle" Möglichkeiten: posix isnan Makro , C ++ 0x- isnanFunktionsvorlage oder visuelle C ++ - _isnanFunktion .

Leider ist es ziemlich unpraktisch zu erkennen, welche davon verwendet werden sollen.

Und leider gibt es keine zuverlässige Möglichkeit, festzustellen, ob Sie eine IEEE 754-Darstellung mit NaNs haben. Die Standardbibliothek bietet einen offiziellen solchen Weg (numeric_limits<double>::is_iec559 ). Aber in der Praxis vermasseln Compiler wie g ++ das.

Theoretisch könnte man einfach verwenden x != x , aber Compiler wie g ++ und Visual C ++ vermasseln das.

Testen Sie also am Ende das Spezifische NaN-Bitmuster , indem Sie eine bestimmte Darstellung wie IEEE 754 annehmen (und hoffentlich irgendwann durchsetzen!).


BEARBEITEN : Als Beispiel für "Compiler wie g ++ ... vermasseln Sie das" betrachten Sie

#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> Geben Sie "C: \ Programme \ @commands \ gnuc.bat" ein.
@rem -finput-charset = windows-1252
@ g ++ -O -pedantic -std = c ++ 98 -Wall -Wwrite-strings% * -Wno-long-long

C: \ test> gnuc x.cpp

C: \ test> ein && Echo funktioniert ... || Echo! fehlgeschlagen
funktioniert ...

C: \ test> gnuc x.cpp --fast-math

C: \ test> ein && Echo funktioniert ... || Echo! fehlgeschlagen
Behauptung fehlgeschlagen: a! = B, Datei x.cpp, Zeile 6

Diese Anwendung hat die Runtime aufgefordert, sie auf ungewöhnliche Weise zu beenden.
Bitte wenden Sie sich an das Support-Team der Anwendung, um weitere Informationen zu erhalten.
!gescheitert

C: \ test> _

4
@Alf: Ihr Beispiel funktioniert wie erwartet für mich unter Mac OS X und Linux unter verschiedenen Versionen von g ++ zwischen 4.0 und 4.5. Die Dokumentation für die -ffast-mathOption besagt ausdrücklich, dass es zu einer falschen Ausgabe für Programme kommen kann, die von einer genauen Implementierung abhängen, wenn IEEE- oder ISO-Regeln / Spezifikationen für mathematische Funktionen vorliegen. Ohne diese Option ist die Verwendung x != xeine absolut gültige und tragbare Methode zum Testen von NaN.
Adam Rosenfield

6
@Adam: Was Sie vermissen ist, dass der C ++ - Standard keine IEEE-Darstellung oder Mathematik für Floats erfordert. Soweit die Manpage Ihnen sagt, gcc -ffast-mathhandelt es sich immer noch um eine konforme C ++ - Implementierung (vorausgesetzt, es wird numeric_limits::is_iec559richtig, obwohl Alf dies oben nicht vorschlägt): C ++ - Code, der auf IEEE basiert, ist kein portables C ++ und hat kein Recht zu erwarten, dass Implementierungen es bereitstellen.
Steve Jessop

5
Und Alf hat recht, schneller Test auf gcc 4.3.4 und is_iec559ist wahr mit -ffast-math. Das Problem hierbei ist also, dass die GCC-Dokumente -ffast-mathnur sagen, dass es sich nicht um IEEE / ISO für mathematische Funktionen handelt, während sie sagen sollten , dass es sich nicht um C ++ handelt, da die Implementierung von nicht numeric_limitskorrekt ist. Ich würde vermuten, dass GCC zum Zeitpunkt der Definition der Vorlage nicht immer feststellen kann, ob das eventuelle Backend tatsächlich konforme Floats hat, und es daher nicht einmal versucht. IIRC gibt es ähnliche Probleme in der ausstehenden Fehlerliste für die C99-Konformität von GCC.
Steve Jessop

1
@Alf, @Steve, ich wusste nicht, dass der C ++ - Standard keine Spezifikation für Gleitkommawerte enthält. Es ist ziemlich schockierend für mich. Es sieht besser aus, IEEE 754 und NaN als plattformspezifische Erweiterung anstelle von Standard zu handhaben. Ist es nicht? Und kann ich erwarten, dass in C ++ 0x irgendeine Art von isnan () oder IEEE754 hinzugefügt wird?
Eonil

3
@Eonil: C ++ 0x hat zum Beispiel noch "Die Wertdarstellung von Fließkommatypen ist implementierungsdefiniert". C und C ++ zielen beide darauf ab, Implementierungen auf Computern ohne Gleitkomma-Hardware zu unterstützen, und ordnungsgemäße IEEE 754-Gleitkommazahlen können viel langsamer zu emulieren sein als einigermaßen genaue Alternativen. Die Theorie ist, dass Sie behaupten können, is_iec559wenn Sie IEEE benötigen, in der Praxis scheint dies bei GCC nicht zu funktionieren. C ++ 0x hat zwar eine isnanFunktion, aber da GCC is_iec559jetzt nicht korrekt implementiert wird , wird es -ffast-mathwahrscheinlich auch nicht in C ++ 0x funktionieren und möglicherweise seine Funktion beschädigen isnan.
Steve Jessop

39

Es gibt ein std :: isnan, wenn Ihr Compiler c99-Erweiterungen unterstützt, aber ich bin mir nicht sicher, ob mingw dies tut.

Hier ist eine kleine Funktion, die funktionieren sollte, wenn Ihr Compiler nicht über die Standardfunktion verfügt:

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

6
warum nicht einfach var! = var?
Brian R. Bondy

8
Wenn dies der Fall ist, optimiert der Compiler den Vergleich und gibt immer true zurück.
CTT

23
Nein, gibt es nicht. Ein Compiler, der das macht, ist kaputt. Sie können auch sagen, dass die Standardbibliothek möglicherweise isnandas falsche Ergebnis zurückgibt. Technisch gesehen könnte der Compiler fehlerhaft sein, aber in der Praxis wird es nicht passieren. Gleich wie var != var. Es funktioniert, weil auf diese Weise IEEE-Gleitkommawerte definiert werden.
Jalf

29
Wenn -ffast-math gesetzt ist, gibt isnan () nicht das richtige Ergebnis für gcc zurück. Natürlich ist diese Optimierung als Verstoß gegen die IEEE-Semantik dokumentiert ...
Matthew Herrmann

Wenn -ffast-math gesetzt ist, ist der Compiler fehlerhaft. Oder besser gesagt, wenn -ffast-math eingestellt ist, sind alle Wetten geschlossen und Sie können sich sowieso nicht auf NaNs verlassen.
Adrian Ratnapala

25

Sie können numeric_limits<float>::quiet_NaN( )in der limitsStandardbibliothek definierte verwenden, um mit zu testen. Es ist eine separate Konstante für definiert 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;
}

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


2
Beachten Sie jedoch, dass numeric_limits in GCC Version 3.2.3 einen Fehler enthält, da für quiet_NaN 0.0 zurückgegeben wird. Spätere Versionen von GCC sind meiner Erfahrung nach in Ordnung.
Nathan Kitchen

@ Nathan: Gut zu wissen. Ich benutze Version 4.3.2, also bin ich weit weg vom Wald.
Bill the Lizard

18

Sie können die isnan()Funktion verwenden, müssen jedoch die C-Mathematikbibliothek einschließen.

#include <cmath>

Da diese Funktion Teil von C99 ist, ist sie nicht überall verfügbar. Wenn Ihr Anbieter die Funktion nicht bereitstellt, können Sie aus Kompatibilitätsgründen auch Ihre eigene Variante definieren.

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

Ich habe <cmath> verwendet und es ist kein Isnan darin! übrigens fand ich heraus , dass es ist ein isnanin <math.h>
hasen

1
Wie gesagt, dies ist Teil von C99. Da C99 nicht Teil eines aktuellen C ++ - Standards ist, habe ich die Alternative bereitgestellt. Da es jedoch wahrscheinlich ist, dass isnan () in einem kommenden C ++ - Standard enthalten sein wird, habe ich eine # ifndef-Direktive um ihn herum eingefügt.
Raimue

12

Der folgende Code verwendet die Definition von NAN (alle Exponentenbits gesetzt, mindestens ein Teilbit gesetzt) ​​und geht davon aus, dass sizeof (int) = sizeof (float) = 4. Sie können NAN in Wikipedia nach Details suchen.

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


Ich glaube, das würde auch auf Big-Endian-Plattformen funktionieren. Das Wörtliche 0x7fffffffwürde einfach in Erinnerung bleiben als ff ff ff 7f. valuehat die gleiche Reihenfolge wie es 0x7f800000, so dass alle Operationen in einer Reihe stehen (es gibt kein Austauschen von Bytes). Es würde mich interessieren, ob jemand dies auf einer Big-Endian-Plattform testen könnte.
Bryan W. Wagner

0x7fff1234ist auch ein NaN. So ist0xffffffff
Steve Hollasch

12

Nanoprävention

Meine Antwort auf diese Frage lautet: Verwenden Sie keine rückwirkenden Prüfungen fürnan . Verwenden Sie stattdessen vorbeugende Überprüfungen für Unterteilungen des Formulars 0.0/0.0.

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

nanErgebnisse aus der Operation 0.f/0.f, oder 0.0/0.0. nanist eine schreckliche Nemesis für die Stabilität Ihres Codes, die sehr sorgfältig erkannt und verhindert werden muss 1 . Die Eigenschaften davon nanunterscheiden sich von normalen Zahlen:

  • nanist giftig, (5 * nan= nan)
  • nanist nichts gleich, nicht einmal sich selbst ( nan! = nan)
  • nannicht größer als alles ( nan!> 0)
  • nanist nicht weniger als alles ( nan! <0)

Die letzten beiden aufgelisteten Eigenschaften sind gegenlogisch und führen zu einem merkwürdigen Verhalten des Codes, das auf Vergleichen mit einer nanZahl beruht (die drittletzte Eigenschaft ist ebenfalls ungerade, aber Sie werden sie wahrscheinlich nie x != x ?in Ihrem Code sehen (es sei denn, Sie überprüfen) für nan (unzuverlässig))).

In meinem eigenen Code habe ich festgestellt, dass nanWerte dazu neigen, schwer zu findende Fehler zu erzeugen. (Beachten Sie, dass dies bei oder nicht der Fall ist . ( <0) gibt zurück , (0 < ) gibt TRUE zurück und sogar ( < ) gibt TRUE zurück. Nach meiner Erfahrung ist das Verhalten des Codes daher häufig immer noch wie gewünscht.)inf-inf-infTRUEinf-infinf

was unter nan zu tun ist

Was unter passieren soll, 0.0/0.0 muss als Sonderfall behandelt werden , aber was Sie tun, muss von den Zahlen abhängen, von denen Sie erwarten, dass sie aus dem Code hervorgehen.

Im obigen Beispiel ist das Ergebnis von ( 0.f/FLT_MIN) im 0Grunde genommen. Möglicherweise möchten Sie stattdessen 0.0/0.0generieren HUGE. Damit,

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.

Wenn x oben wäre 0.f, infwürde sich dies ergeben (was ein ziemlich gutes / zerstörungsfreies Verhalten aufweist, wie oben tatsächlich erwähnt).

Denken Sie daran, dass die Ganzzahldivision durch 0 eine Laufzeitausnahme verursacht . Sie müssen also immer nach einer ganzzahligen Division durch 0 suchen. Nur weil 0.0/0.0leise ausgewertet wird, nanheißt das nicht, dass Sie faul sein können und nicht nachsehen müssen, 0.0/0.0bevor dies geschieht.

1 Überprüfungen auf nanVia x != xsind manchmal unzuverlässig ( x != xwerden von einigen optimierenden Compilern entfernt, die die IEEE-Konformität verletzen, insbesondere wenn der -ffast-mathSwitch aktiviert ist).


Vielen Dank für den Hinweis; Eine solche Programmierung würde definitiv bei dem Problem als solchem ​​helfen. Versuchen Sie jedoch beim nächsten Mal, die Textformatierungsfunktionen nicht zu stark zu missbrauchen. Das Wechseln von Schriftgröße, Gewicht und Stil erschwert das Lesen.
Magnus

4
Beachten Sie, dass 0.0 / 0.0 nicht die einzige Operation ist, die zu einem NaN führen kann. Die Quadratwurzel einer negativen Zahl gibt NaN zurück. Der Cosinus von + unendlich gibt auch NaN zurück. Die Operation acos (x), bei der x nicht im Bereich [0, pi] liegt, kann ebenfalls zu NaN führen. Kurz gesagt, man muss besonders vorsichtig sein, um auch diese potenziell riskanten Operationen zu betrachten, nicht nur auf 0,0 / 0,0.
Boris Dalstein

Stimme Boris voll und ganz zu. Nach meiner Erfahrung stammte NaN praktisch immer von so etwas wie sqrt (-1.302e-53), dh Zwischenberechnungsergebnisse nahe Null, die in sqrt eingespeist wurden, ohne auf Negativität zu prüfen.
Hans_meine

1
"Verhindern von NaNs" bedeutet, dass Sie in alle grundlegenden arithmetischen Operationen einsteigen müssen, nicht nur in die Division. Sie müssen unter anderem auf ∞ / ∞, 0 * ∞, ∞% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0 achten. Wenn Sie mit solchen grundlegenden Rechenoperationen "vorbeugend" sind, bedeutet dies, dass Sie Ihre Leistung vollständig verbessern (und wahrscheinlich zusätzliche Fälle verpassen, an die Sie nicht gedacht haben).
Steve Hollasch

11

Ab C ++ 14 gibt es verschiedene Möglichkeiten, um zu testen, ob eine Gleitkommazahl valueeine 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::isnanund die häufig vorgeschlagene Überprüfung v != vfunktionieren 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 != vTest 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. isnanAnscheinend 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_nanin 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-mathOption von g ++ fehlgeschlagen . Nur direkte Bitmusterprüfungen funktionieren zuverlässig.


7
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)8 ist.

Während der Laufzeit ist es nur ein Vergleich, Gussteile brauchen keine Zeit. Es wird lediglich die Konfiguration der Vergleichsflags geändert, um die Gleichheit zu überprüfen.


Beachten Sie auch, dass es auf die IEEE 754-Darstellung beschränkt ist.
Prost und hth. - Alf

Beachten Sie, dass diese Umwandlung gegen die strenge Aliasing-Regel von g ++ verstößt und dass der Compiler bekanntermaßen Unmentionable Things ™ ausführt, wenn er formale UB erkennt. Anstelle effizienter Casts müssen Sie mit g ++ sicherheitshalber memcpyein Byte-Array verwenden. Code dafür in meiner Antwort Nr. 2 .
Prost und hth. - Alf

4

Eine mögliche Lösung, die nicht von der spezifischen IEEE-Darstellung für verwendetes NaN abhängt, 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) );
}

Gleitkommazahlen mit einfacher Genauigkeit haben über 8 Millionen legitime und unterschiedliche Bitrepräsentationen für NaN, sodass Sie weitere Vergleiche hinzufügen müssen. :)
Steve Hollasch

4

In Anbetracht dessen, dass (x! = X) für NaN nicht immer garantiert ist (z. B. wenn die Option -ffast-math verwendet wird), habe ich Folgendes verwendet:

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

Zahlen können nicht sowohl <0 als auch> = 0 sein, daher besteht diese Prüfung nur dann, wenn die Zahl weder kleiner noch größer oder gleich Null ist. Welches ist im Grunde überhaupt keine Zahl oder NaN.

Sie können dies auch verwenden, wenn Sie Folgendes bevorzugen:

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

Ich bin mir nicht sicher, wie sich dies auf -ffast-math auswirkt, daher kann Ihr Kilometerstand variieren.


Dies ist tatsächlich fehlerhaft, genauso wie f != fes auch fehlerhaft ist. Ich habe gesehen, wie llvm einen fast identischen Code entfernt hat. Der Optimierer kann die Informationen über den ersten Vergleich weitergeben und herausfinden, dass der zweite Vergleich möglicherweise niemals wahr ist, wenn der erste Vergleich vorliegt. (Wenn der Compiler die IEEE-Regeln strikt einhält, f != fist das sowieso viel einfacher)
Markus

Funktioniert nicht mit der -ffast-mathOption von g ++ . Funktioniert mit Visual C ++. Siehe ( stackoverflow.com/a/42138465/464581 ).
Prost und hth. - Alf

3

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

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

Dies ist eine der besten Antworten auf diese Frage! Ich danke Ihnen für das Teilen.
Henri Menke

2
Andere Antworten weisen darauf hin, dass dies mit der Option -ffast-math fehlschlagen kann.
Technophile

3

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


1

Es scheint mir, dass der beste wirklich plattformübergreifende Ansatz darin besteht, eine Union zu verwenden und das Bitmuster des Double zu testen, um nach NaNs zu suchen.

Ich habe diese Lösung nicht gründlich getestet, und es gibt möglicherweise eine effizientere Möglichkeit, mit den Bitmustern zu arbeiten, aber ich denke, dass sie funktionieren sollte.

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

Beachten Sie, dass "es ein undefiniertes Verhalten ist, von einem Gewerkschaftsmitglied zu lesen, das zuletzt nicht geschrieben wurde". Daher unionfunktioniert diese Verwendung eines Wortspiels zwischen zwei Typen möglicherweise nicht wie gewünscht (: sad_panda :). Der richtige (wenn auch nicht ganz so portable wie gewünschte) Weg wäre, die Vereinigung insgesamt zu vermeiden und ein Memcpy von der doublein eine andere uint64_tVariable durchzuführen und dann den Test mit dieser Hilfsvariablen durchzuführen.
Eljay

0

Unter x86-64 können Sie extrem schnelle Methoden zum Überprüfen auf NaN und unendlich verwenden, die unabhängig von der -ffast-mathCompileroption funktionieren . ( f != f, std::isnan, std::isinfErgeben immer falsemit -ffast-math).


Das Testen auf NaN, unendlich und endliche Zahlen kann leicht durchgeführt werden, indem nach dem maximalen Exponenten gesucht wird. unendlich ist maximaler Exponent mit Mantisse Null, NaN ist maximaler Exponent und Mantisse ungleich Null. Der Exponent wird in den nächsten Bits nach dem obersten Vorzeichenbit gespeichert, so dass wir die Verschiebung nach links verschieben können, um das Vorzeichenbit zu entfernen und den Exponenten zu den obersten Bits zu machen. Es ist keine Maskierung ( operator&) erforderlich:

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 stdVersionen von isinfund isfiniteLaden von 2 double/floatKonstanten aus dem .dataSegment und im schlimmsten Fall können sie 2 Datencache-Fehler verursachen. Die obigen Versionen laden keine Daten inf_double_shl1und inf_float_shl1Konstanten werden als unmittelbare Operanden in die Montageanweisungen codiert.


Schneller isnan2sind nur 2 Montageanleitungen:

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 der ucomisdBefehl das Paritätsflag setzt, wenn ein Argument NaN ist. So std::isnanfunktioniert es, wenn keine -ffast-mathOptionen angegeben sind.


-1

Der IEEE-Standard sagt, wenn der Exponent alle 1s ist und die Mantisse nicht Null ist, ist die Zahl a NaN. Double ist 1Vorzeichenbit, 11Exponentenbit und 52Mantissenbit. Mach ein bisschen nach.


-3

Wie oben erwähnt, funktioniert a! = A in g ++ und einigen anderen Compilern nicht, aber dieser Trick sollte. Es mag nicht so effizient sein, aber es ist immer noch ein Weg:

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

Grundsätzlich druckt printf in g ++ (bei anderen bin ich mir allerdings nicht sicher) 'nan' in den Formaten% d oder% .f, wenn die Variable keine gültige Ganzzahl / float ist. Daher prüft dieser Code, ob das erste Zeichen der Zeichenfolge 'n' ist (wie in "nan").


2
Würde das nicht einen Pufferüberlauf verursachen, wenn a = 234324.0f?
Mazyod

Ja, oder 340282346638528859811704183484516925440.000wenn a = FLT_MAX. Er müsste verwenden char s[7]; sprintf(s, "%.0g", a);, das werden 6 chrs sein, wenn a=-FLT_MAX, oder-3e+38
Bobobobo

-3

Dies erkennt Unendlichkeit und auch NaN in Visual Studio, indem überprüft wird, ob es innerhalb doppelter Grenzen liegt:

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

Überprüfen Sie die Definition von FLT_MIN, DBL_MINund LDBL_MINvorsichtiger. Dies sind die kleinsten normalisierten Werte für jeden Typ. Beispielsweise hat die einfache Genauigkeit über 8 Millionen legitime Denorm-Werte, die größer als Null und kleiner als FLT_MIN(und nicht NaN) sind.
Steve Hollasch
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.