Warum verhalten sich Vergleiche von NaN-Werten anders als alle anderen Werte? Das heißt, alle Vergleiche mit den Operatoren ==, <=,> =, <,>, bei denen einer oder beide Werte NaN sind, geben entgegen dem Verhalten aller anderen Werte false zurück.
Ich nehme an, dies vereinfacht numerische Berechnungen in gewisser Weise, aber ich konnte keinen explizit angegebenen Grund finden, nicht einmal in den Lecture Notes zum Status von IEEE 754 von Kahan, in denen andere Entwurfsentscheidungen ausführlich erörtert werden.
Dieses abweichende Verhalten verursacht Probleme bei der einfachen Datenverarbeitung. Wenn ich zum Beispiel eine Liste von Datensätzen anhand eines reellen Feldes in einem C-Programm sortiere, muss ich zusätzlichen Code schreiben, um NaN als maximales Element zu behandeln, da sonst der Sortieralgorithmus verwirrt werden kann.
Bearbeiten: Die bisherigen Antworten argumentieren alle, dass es bedeutungslos ist, NaNs zu vergleichen.
Ich stimme zu, aber das bedeutet nicht, dass die richtige Antwort falsch ist, sondern es wäre ein Not-a-Boolean (NaB), der zum Glück nicht existiert.
Die Wahl der Rückgabe von wahr oder falsch für Vergleiche ist meines Erachtens willkürlich, und für die allgemeine Datenverarbeitung wäre es vorteilhaft, wenn die üblichen Gesetze (Reflexivität von ==, Trichotomie von <, ==,>) eingehalten würden, damit keine Datenstrukturen entstehen die sich auf diese Gesetze stützen, werden verwirrt.
Ich bitte um einen konkreten Vorteil, diese Gesetze zu brechen, nicht nur um philosophisches Denken.
Edit 2: Ich denke, ich verstehe jetzt, warum es eine schlechte Idee wäre, NaN maximal zu machen, es würde die Berechnung der Obergrenzen durcheinander bringen.
NaN! = NaN kann wünschenswert sein, um zu vermeiden, dass Konvergenz in einer Schleife wie z
while (x != oldX) {
oldX = x;
x = better_approximation(x);
}
Dies sollte jedoch besser geschrieben werden, indem die absolute Differenz mit einer kleinen Grenze verglichen wird. Meiner Meinung nach ist dies ein relativ schwaches Argument, um die Reflexivität bei NaN zu brechen.
quelle
while (fabs(x - oldX) > threshold)
Verlassen der Schleife werden, wenn Konvergenz auftritt oder ein NaN in die Berechnung eintritt. Der Nachweis des NaN und eines geeigneten Mittels würde dann außerhalb der Schleife erfolgen.Antworten:
Ich war Mitglied des IEEE-754-Komitees. Ich werde versuchen, die Dinge ein wenig zu klären.
Zunächst einmal sind Gleitkommazahlen keine reellen Zahlen, und Gleitkomma-Arithmetik erfüllt nicht die Axiome der reellen Arithmetik. Die Trichotomie ist nicht die einzige Eigenschaft der realen Arithmetik, die weder für Floats noch für die wichtigste gilt. Beispielsweise:
Ich könnte weitermachen Es ist nicht möglich, einen arithmetischen Typ fester Größe anzugeben, der alle Eigenschaften der realen Arithmetik erfüllt , die wir kennen und lieben. Das 754-Komitee muss beschließen, einige von ihnen zu biegen oder zu brechen. Dies wird von einigen ziemlich einfachen Prinzipien geleitet:
In Bezug auf Ihren Kommentar "Das bedeutet nicht, dass die richtige Antwort falsch ist" ist dies falsch. Das Prädikat
(y < x)
fragt, oby
es kleiner als istx
. Wenny
NaN ist, ist es nicht kleiner als ein Gleitkommawertx
, daher ist die Antwort notwendigerweise falsch.Ich erwähnte, dass die Trichotomie nicht für Gleitkommawerte gilt. Es gibt jedoch eine ähnliche Eigenschaft, die gilt. Abschnitt 5.11, Absatz 2 der Norm 754-2008:
Wenn Sie zusätzlichen Code für den Umgang mit NaNs schreiben, ist es normalerweise möglich (wenn auch nicht immer einfach), Ihren Code so zu strukturieren, dass NaNs ordnungsgemäß durchfallen. Dies ist jedoch nicht immer der Fall. Wenn dies nicht der Fall ist, ist möglicherweise ein zusätzlicher Code erforderlich, aber dies ist ein geringer Preis für die Bequemlichkeit, die das algebraische Schließen der Gleitkomma-Arithmetik gebracht hat.
Nachtrag: Viele Kommentatoren haben argumentiert, dass es sinnvoller wäre, die Reflexivität von Gleichheit und Trichotomie zu bewahren, da die Übernahme von NaN! = NaN kein bekanntes Axiom zu bewahren scheint. Ich gebe zu, Sympathie für diesen Standpunkt zu haben, deshalb dachte ich, ich würde diese Antwort noch einmal überdenken und ein bisschen mehr Kontext bieten.
Mein Verständnis aus dem Gespräch mit Kahan ist, dass NaN! = NaN aus zwei pragmatischen Überlegungen hervorgegangen ist:
Das
x == y
sollte nicht höher sein ,x - y == 0
wenn möglich (über einen Satz von realer Arithmetik zu sein, das macht Hardware - Implementierung des Vergleiches platzsparender, die zu der Zeit von größter Bedeutung war der Standard entwickelt wurde - jedoch zu beachten, dass dies für x verletzt = y = unendlich, es ist also kein guter Grund für sich; es hätte vernünftigerweise dazu neigen können(x - y == 0) or (x and y are both NaN)
).Noch wichtiger ist, dass es
isnan( )
zum Zeitpunkt der Formalisierung von NaN in der 8087-Arithmetik kein Prädikat gab . Es war notwendig, Programmierern ein bequemes und effizientes Mittel zur Erkennung von NaN-Werten zur Verfügung zu stellen, die nicht von Programmiersprachen abhingen, die so etwas wieisnan( )
viele Jahre dauern konnten. Ich zitiere Kahans eigenes Schreiben zu diesem Thema:Beachten Sie, dass dies auch die Logik ist, die die Rückgabe eines „Not-A-Boolean“ ausschließt. Vielleicht war dieser Pragmatismus fehl am Platz, und der Standard hätte es erfordern müssen
isnan( )
, aber das hätte es fast unmöglich gemacht, NaN mehrere Jahre lang effizient und bequem zu nutzen, während die Welt auf die Einführung der Programmiersprache wartete. Ich bin nicht davon überzeugt, dass dies ein vernünftiger Kompromiss gewesen wäre.Um ehrlich zu sein: Das Ergebnis von NaN == NaN wird sich jetzt nicht ändern. Es ist besser zu lernen, damit zu leben, als sich im Internet zu beschweren. Wenn Sie , dass eine Bestellung Beziehung geeignet für Behälter argumentieren will , sollte auch vorhanden ist , würde ich empfehlen , dafür ein, dass Ihre Lieblings - Programmiersprache die Umsetzung
totalOrder
Prädikat standardisiert in IEEE-754 (2008). Die Tatsache, dass es noch nicht für die Gültigkeit von Kahans Besorgnis spricht, die den aktuellen Stand der Dinge motiviert hat.quelle
1f/3f == 10000001f/30000002f
? Wenn Gleitkommawerte als Äquivalenzklassen betrachtet werden,a=b
bedeutet dies nicht "Die Berechnungen, die sich ergabena
undb
bei unendlicher Genauigkeit identische Ergebnisse liefern würden", sondern "Was bekannt ist,a
stimmt mit dem überein, was bekannt ist."b
". Ich bin gespannt, ob Sie Beispiele für Code kennen, bei denen "Nan! = NaN" die Dinge einfacher macht als sonst?!(x < 0 || x == 0 || x > 0)
, aber es wäre langsamer und ungeschickter gewesen alsx != x
.NaN kann als undefinierter Zustand / undefinierte Zahl betrachtet werden. ähnlich dem Konzept, dass 0/0 undefiniert oder sqrt (-3) ist (im reellen Zahlensystem, in dem der Gleitkomma lebt).
NaN wird als eine Art Platzhalter für diesen undefinierten Zustand verwendet. Mathematisch gesehen ist undefiniert nicht gleich undefiniert. Sie können auch nicht sagen, dass ein undefinierter Wert größer oder kleiner als ein anderer undefinierter Wert ist. Daher geben alle Vergleiche false zurück.
Dieses Verhalten ist auch in den Fällen vorteilhaft, in denen Sie sqrt (-3) mit sqrt (-2) vergleichen. Sie würden beide NaN zurückgeben, aber sie sind nicht äquivalent, obwohl sie den gleichen Wert zurückgeben. Daher ist es das gewünschte Verhalten, wenn Gleichheit beim Umgang mit NaN immer falsch ist.
quelle
!=
Operator gibt true zurück. WennNaN==NaN
undNaN!=NaN
beide false zurückgeben, kann Code, der x und y vergleicht, auswählen, was passieren soll, wenn beide Operanden NaN sind, indem entweder==
oder ausgewählt wird!=
.Noch eine Analogie einbringen. Wenn ich Ihnen zwei Schachteln reiche und Ihnen sage, dass keine von ihnen einen Apfel enthält, würden Sie mir dann sagen, dass die Schachteln dasselbe enthalten?
NaN enthält keine Informationen darüber, was etwas ist, nur was es nicht ist. Daher kann man niemals definitiv sagen, dass diese Elemente gleich sind.
quelle
(NaN==Nan)==false
. Was ich nicht verstehe, ist die Begründung dafür(Nan!=Nan)==true
.Aus dem Wikipedia-Artikel über NaN geht hervor , dass die folgenden Praktiken NaNs verursachen können:
Da es keine Möglichkeit gibt zu wissen, welche dieser Operationen das NaN erzeugt haben, gibt es keine Möglichkeit, sie zu vergleichen, was Sinn macht.
quelle
Ich kenne die Designgründe nicht, aber hier ist ein Auszug aus dem IEEE 754-1985-Standard:
"Es soll möglich sein, Gleitkommazahlen in allen unterstützten Formaten zu vergleichen, auch wenn sich die Formate der Operanden unterscheiden. Vergleiche sind exakt und laufen weder über noch unter. Vier sich gegenseitig ausschließende Beziehungen sind möglich: kleiner als, gleich, größer als und ungeordnet Der letzte Fall tritt auf, wenn mindestens ein Operand NaN ist. Jedes NaN soll ungeordnet mit allem vergleichen, einschließlich sich selbst. "
quelle
Es sieht nur merkwürdig aus, weil die meisten Programmierumgebungen, die NaNs zulassen, keine 3-wertige Logik zulassen. Wenn Sie eine 3-wertige Logik in die Mischung einbringen, wird sie konsistent:
Selbst .NET bietet keinen
bool? operator==(double v1, double v2)
Operator, sodass Sie immer noch am dummen(NaN == NaN) = false
Ergebnis festhalten .quelle
Ich vermute, dass NaN (Not A Number) genau das bedeutet: Dies ist keine Zahl, und daher macht ein Vergleich keinen Sinn.
Es ist ein bisschen wie Arithmetik in SQL mit
null
Operanden: Sie alle führen zunull
.Die Vergleiche für Gleitkommazahlen vergleichen numerische Werte. Daher können sie nicht für nicht numerische Werte verwendet werden. NaN kann daher nicht im numerischen Sinne verglichen werden.
quelle
FOO
. Damit NaN gleichwertig ist,if (NaN != NaN) foo();
sollte es nicht ausgeführt werdenfoo
, aber es tut es.Die stark vereinfachte Antwort lautet, dass ein NaN keinen numerischen Wert hat, sodass nichts zu vergleichen ist.
Sie können Ihre NaNs testen und durch + INF ersetzen, wenn Sie möchten, dass sie sich wie + INF verhalten.
quelle
Ich stimme zwar zu, dass Vergleiche von NaN mit einer reellen Zahl ungeordnet sein sollten, aber ich denke, es gibt nur Grund, NaN mit sich selbst zu vergleichen. Wie entdeckt man zum Beispiel den Unterschied zwischen signalisierenden NaNs und leisen NaNs? Wenn wir uns die Signale als eine Menge von Booleschen Werten (dh einen Bitvektor) vorstellen, könnte man sich fragen, ob die Bitvektoren gleich oder verschieden sind, und die Mengen entsprechend anordnen. Wenn beispielsweise beim Decodieren eines maximal vorgespannten Exponenten der Signifikand nach links verschoben würde, um das höchstwertige Bit des Signifikanten auf dem höchstwertigen Bit des Binärformats auszurichten, wäre ein negativer Wert ein leises NaN und jeder positive Wert wäre ein signalisierendes NaN sein. Null ist natürlich für unendlich reserviert und der Vergleich wäre ungeordnet. Die MSB-Ausrichtung würde den direkten Vergleich von Signalen auch aus verschiedenen Binärformaten ermöglichen. Zwei NaNs mit demselben Satz von Signalen wären daher äquivalent und würden der Gleichheit Bedeutung verleihen.
quelle
Für mich ist der einfachste Weg, dies zu erklären:
Sie können NaN nicht mit etwas anderem vergleichen (auch nicht mit sich selbst), da es keinen Wert hat. Es kann auch ein beliebiger Wert sein (außer einer Zahl).
quelle
Weil Mathematik das Feld ist, in dem Zahlen "nur existieren". Bei der Berechnung müssen Sie initialisieren , diese Zahlen und halten ihren Zustand nach Ihren Bedürfnissen. In jenen alten Tagen funktionierte die Speicherinitialisierung so, wie man sich nie verlassen konnte. Sie könnten sich niemals erlauben, darüber nachzudenken "Oh, das würde die ganze Zeit mit 0xCD initialisiert werden, mein Algo wird nicht kaputt gehen" .
Sie benötigen also ein geeignetes nicht mischendes Lösungsmittel, das klebrig genug ist , damit Ihr Algorithmus nicht angesaugt und beschädigt wird. Gute Algorithmen mit Zahlen funktionieren meistens mit Relationen, und solche if () -Relationen werden weggelassen.
Dies ist nur Fett, das Sie bei der Erstellung in eine neue Variable einfügen können, anstatt eine zufällige Hölle aus dem Computerspeicher zu programmieren. Und Ihr Algorithmus, was auch immer er ist, wird nicht kaputt gehen.
Wenn Sie dann immer noch plötzlich feststellen, dass Ihr Algorithmus NaNs produziert, können Sie diese bereinigen und nacheinander in jeden Zweig schauen. Auch hier hilft die "immer falsche" Regel sehr.
quelle
Sehr kurze Antwort:
Denn folgendes:
nan / nan = 1
darf NICHT halten. Sonstinf/inf
wäre 1.(Daher
nan
kann nicht gleich seinnan
. Was>
oder<
betrifft, wennnan
wir eine Ordnungsrelation in einer Menge respektieren würden, die die archimedische Eigenschaft erfüllt, hätten wir wiedernan / nan = 1
an der Grenze).quelle
inf = inf
undinf / inf = nan
werden esnan = nan
auch nicht verhindernnan / nan = nan
.nan / nan = 1
? Wie auch immer ... Ihre Argumentation macht Sinn, wenn inf und nan genau wie alle anderen Zahlen wären. Das ist nicht der Fall. Der Grund dafürinf/inf
muss seinnan
(oder eine unbestimmte Form in der Mathematik) und ist nicht1
subtiler als eine einfache algebraische Manipulation (siehe Satz von De L'Hospital).