NaN == NaN?



  • Shade Of Mine schrieb:

    Christoph schrieb:

    Etwas unschön finde ich, dass aus dem Grund std::sort auf Fließkomma-Sequenzen nicht benutzbar ist. Das sind ziemlich hohe Kosten, nur um (NaN != NaN) == true zu haben.

    Wo würdest du denn NaN hinschieben?

    Ist NaN kleiner als 7? Oder größer als -5?

    Das hängt vom Programm ab. Sinnvoll wäre vermutlich ganz vorne oder ganz hinten.

    knivil schrieb:

    Schreibe dir doch einen separate Vergleichsfunktion ... Ich sehe da kein Problem.

    Eine eigene Vergleichsfunktion hat zwei Nachteile:
    1. Sie ist mehr Implementierungsaufwand und mehr Code, der gewartet werden muss.
    2. Sie ist langsamer verglichen mit einem reinen Vergleich in Hardware.

    Ich bin kein Numeriker, aber sortierbar zu sein halte ich für eine wünschenswerte Eigenschaft. Und wenn NaN eben ganz hinten landet, dann ist das denke ich besser, als wenn die Sortier-Funktion in eine Endlos-Schleife geht, nur weil man remove_if oder die eigene Vergleichsfunktion vergessen hat. Das ganze NaN-Verhalten ist IMHO eine Verletzung des "Principle of least astonishment".

    NaNs sind infektiös: Wenn irgendwo in einem Ausdruck NaN auftaucht, taucht am Ende wahrscheinlich auch NaN auf. Und aus dem NaN bekommt man erschreckend wenig Information. Man erfährt im Grunde nur "irgendwas ist schiefgegangen". Ob es sqrt(negative_zahl) oder Division durch 0 oder was auch immer war, erfährt man nicht.
    Wären aus heutiger Sicht C++-Exceptions (oder analoge Konzepte in anderen Sprachen) vielleicht geeigneter, weil man damit mehr Information über den aufgetretenen Fehler übermitteln kann?



  • Christoph schrieb:

    Und aus dem NaN bekommt man erschreckend wenig Information. Man erfährt im Grunde nur "irgendwas ist schiefgegangen". Ob es sqrt(negative_zahl) oder Division durch 0 oder was auch immer war, erfährt man nicht.

    Man könnte ja in NaNs mehrere NaN-Zustände kodieren (2^23 - 1 bei einem IEEE 754 single). Wird z.B. für die Unterscheidung signaling/silent NaNs schon benutzt. Aber wirklich Sinn macht das ja auch nicht.



  • In IEEE754 gibt es Exceptions. Nur heißen die da Traps. Und was in IEEE754 Exception heißt sind Flags.

    Der Typ hinter IEEE754 hat viel dazu
    http://www.eecs.berkeley.edu/~wkahan/

    zB S.20ff http://www.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf



  • otze schrieb:

    Christoph schrieb:

    NaN != NaN ist also sehr "unmathematisch", in gewisser Weise.

    Ja, aber sonst wär sowas möglich:

    (0.0/0.0)==sqrt(-1) => true

    Auch nicht wirklich Mathematisch.

    Ich sehe das ganze pragmatisch: Für mich heißt NaN in einem Programm "hier ist ein Fehler aufgetreten". Man kann mit NaN sowieso nicht sinnvoll weiterrechnen und ich kenne keinen Fall, in dem NaN ein Wert in einem Programm ist, mit dem weitergerechnet wird. NaN tritt auf, wenn man einen Programmierfehler im Code hat.

    Wann vergleicht man dann zwei NaNs mit dem Gleichheitsoperator? So etwas, wie Du oben stehen hast, kommt eigentlich nie vor. Denn das wäre aus meiner Sicht eine Variante von "2 Programmierfehler in einer Zeile, die beide das gleiche Resultat haben".

    Ich hatte letzt einen Programmierfehler, der NaNs produziert hat. Ich habe mir die die Werte ausgegeben und unter den 50000 Werten, die ich produziert habe, gab es ein paar mal NaN in der Ausgabe zu lesen. Ok. Ich wusste nicht, dass NaN!=NaN und wollte näher an den Fehler rankommen. Ich checkte also mit "a==NaN?" in einer Schleife, wann der Fehler auftritt und natürlich trat er erstmal nie auf. Zumindest habe ich einige Zeit gebraucht, um herauszufinden, was da schief läuft. Ich brauchte sicherlich eine Stunde, um herauszufinden, dass man bezüglich NaN nicht mit "a==NaN?" testen kann. Danach war die Fehlersuche dann trivial. Ich konnte mich problemlos von einer Methode zur nächsten vorarbeiten, bis ich schließlich an der Stelle angekommen war, an der das NaN entstand.

    Man kann sagen, dass mir da einfach Wissen gefehlt hat. "Man muss halt einfach wissen, dass man anders auf NaN checkt!". Aber irgendwie ist das so ein Punkt gewesen, der mich einfach überrascht. Es lag bitweise Gleichheit vor. Und deshalb war es für mich erstmal unerklärlich, warum der Gleichheitsoperator dann nicht true zurückliefert. Um es auf den Punkt zu bringen: Für mich ist das einfach unintuitiv.

    Und Begründungen der Art "(0.0/0.0)==sqrt(-1)" halte ich in dem Fall nicht für sinnvoll. Derartige Begründungen setzen voraus, dass man etwas sinnvolles mit NaN machen kann. Aus meiner Sicht ist aber das einzig sinnvolle, was man mit NaN machen kann, das Feststellen, dass dieser Wert aufgetreten ist und dann entsprechend darauf zu reagieren. Zudem gehst Du von einer bestimmten Logik im normalen Code aus. Du meinst, die Leute gucken, ob zwei Werte gleich sind. Es kommt aber genauso häufig vor, dass sie testen, ob zwei Werte ungleich sind. Bzw. bei Fließkommawerten sollten sie meistens gar nicht mit == oder != vergleichen. Und ich sehe nicht, inwiefern false bei "NaN==NaN?" ein sinnvollerer Rückgabewert ist als true.



  • Gregor schrieb:

    Für mich ist das einfach unintuitiv.

    das mag sein, aber wie will man es anders lösen? bedenke dass es unterschiedliche NaNs geben kann.

    Und ich sehe nicht, inwiefern false bei "NaN==NaN?" ein sinnvollerer Rückgabewert ist als true.

    weils die fehlersuche erleichtert.

    wenn du nämlich irgendwo testest ob eh alles passt
    if(a!=b) error();
    und mindestens ein NaN dabei -> error.

    bei (NaN==NaN)==true stolperst du in viele fallen rein. weil nämlich plötzlich dein kompletter code durchlaufen kann und keine fehler findet - aber das ergebnis schrott ist. bei (NaN==NaN)==false kann dir das nicht passieren. mal von der nichttrivialität eines ==NaN abgesehen.

    auf NaN testen ist nämlich weit mehr als nur ein ==



  • Christoph schrieb:

    NaNs sind infektiös: Wenn irgendwo in einem Ausdruck NaN auftaucht, taucht am Ende wahrscheinlich auch NaN auf. Und aus dem NaN bekommt man erschreckend wenig Information. Man erfährt im Grunde nur "irgendwas ist schiefgegangen". Ob es sqrt(negative_zahl) oder Division durch 0 oder was auch immer war, erfährt man nicht.
    Wären aus heutiger Sicht C++-Exceptions (oder analoge Konzepte in anderen Sprachen) vielleicht geeigneter, weil man damit mehr Information über den aufgetretenen Fehler übermitteln kann?

    Der Sinn ist ja, dass am Ende auch NaN rauskommt. Wenn man erfahren will, was für ein Fehler aufgetreten ist, dann muss man die Flags ansehen man: fetestexcept. Leider bietet C99/C++0x wohl keinen Support für das Setzen von Traphandlern an. Mit der GLIBC kann man aber immerhin ein SIGFPE auslösen lassen

    #include <iostream>
    #include <cmath>
    #include <cfenv>
    
    int main() {
      feenableexcept(FE_INVALID); // oder FE_ALL_EXCEPT für alles
      double d = std::sqrt(-1.0); // -> SIGFPE
      std::cout << d << ' ' << std::boolalpha << std::isnan(d) << std::endl;
    }
    

    http://www.gnu.org/s/libc/manual/html_node/Control-Functions.html



  • Shade Of Mine schrieb:

    bedenke dass es unterschiedliche NaNs geben kann.

    Warum hat man das eigentlich so gemacht? Hab Ihr irgendwann schon mal zwischen NaNs unterschieden?



  • Gregor schrieb:

    Shade Of Mine schrieb:

    bedenke dass es unterschiedliche NaNs geben kann.

    Warum hat man das eigentlich so gemacht? Hab Ihr irgendwann schon mal zwischen NaNs unterschieden?

    Ich glaube das ganze floating point zeugs ist ziemlich zusammen gewürfelt. jedenfalls verstehe ich die hälfte der sachen nicht wirklich. vermutlich sogar mehr :p

    unterschiedliche NaNs zeigen halt unterschiedliche situationen an wo sie entstanden sind. für mich war das nie relevant, nan ist fehler und debugger macht den rest. aber in gewisserweise ist es schon verständlich dass man da noch ein paar fehlercodes reinpackt.



  • Gregor schrieb:

    Ich sehe das ganze pragmatisch: Für mich heißt NaN in einem Programm "hier ist ein Fehler aufgetreten". Man kann mit NaN sowieso nicht sinnvoll weiterrechnen und ich kenne keinen Fall, in dem NaN ein Wert in einem Programm ist, mit dem weitergerechnet wird. NaN tritt auf, wenn man einen Programmierfehler im Code hat.

    Nö, NaN kann ja auch bedeuten, dass man "komische" Messdaten reinbekommt. Ist natürlich die Frage was man da macht.

    Gregor schrieb:

    Ich hatte letzt einen Programmierfehler, der NaNs produziert hat. Ich habe mir die die Werte ausgegeben und unter den 50000 Werten, die ich produziert habe, gab es ein paar mal NaN in der Ausgabe zu lesen. Ok. Ich wusste nicht, dass NaN!=NaN und wollte näher an den Fehler rankommen. Ich checkte also mit "a==NaN?" in einer Schleife, wann der Fehler auftritt und natürlich trat er erstmal nie auf. Zumindest habe ich einige Zeit gebraucht, um herauszufinden, was da schief läuft. Ich brauchte sicherlich eine Stunde, um herauszufinden, dass man bezüglich NaN nicht mit "a==NaN?" testen kann. Danach war die Fehlersuche dann trivial. Ich konnte mich problemlos von einer Methode zur nächsten vorarbeiten, bis ich schließlich an der Stelle angekommen war, an der das NaN entstand.

    Normalerweise hat man doch kein anderes NaN verfügbar oder hast du a == sqrt(-1.0) gemacht?



  • Shade Of Mine schrieb:

    Ich glaube das ganze floating point zeugs ist ziemlich zusammen gewürfelt. jedenfalls verstehe ich die hälfte der sachen nicht wirklich. vermutlich sogar mehr :p

    Nein, das meiste stammt wohl von W. Kahan und der hat sich schon viele Gedanken dazu gemacht.

    Siehe

    HalloWelt schrieb:

    In IEEE754 gibt es Exceptions. Nur heißen die da Traps. Und was in IEEE754 Exception heißt sind Flags.

    Der Typ hinter IEEE754 hat viel dazu
    http://www.eecs.berkeley.edu/~wkahan/

    zB S.20ff http://www.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf



  • rüdiger schrieb:

    Normalerweise hat man doch kein anderes NaN verfügbar oder hast du a == sqrt(-1.0) gemacht?

    In Java gibt es die Konstante Float.NaN. Hmmm... vielleicht sollte es die besser doch nicht geben. 😃

    EDIT: In dem Zusammenhang ist auch das Verhalten von Float.equals in Java interessant:

    Note that in most cases, for two instances of class Float, f1 and f2, the value of f1.equals(f2) is true if and only if

    f1.floatValue() == f2.floatValue()

    also has the value true. However, there are two exceptions:

    • If f1 and f2 both represent Float.NaN, then the equals method returns true, even though Float.NaN==Float.NaN has the value false.
    • If f1 represents +0.0f while f2 represents -0.0f, or vice versa, the equal test has the value false, even though 0.0f==-0.0f has the value true.

    This definition allows hash tables to operate properly.



  • Die Definition von equals ist für Java notwendig, da man sonst keine Möglichkeit hätte, Float/Double konsequent als key zu verwenden. Würde equals für NaN.equals NaN false zurückliefern, wäre dieser key tot und man könnte an die Werte höchstens noch mittels iterator rankommen.

    Das betrifft aber auch nur die Wrapper Klassen. Das Verhalten der Primitive ist für mathematische Operationen ausschlaggebend.



  • Tim schrieb:

    Errrrm, nein. a ist inf, b ist inf. inf == inf gilt. Diff ist NaN weil Inf - Inf.

    Das ist mir durchaus klar. Was ich bloß damit sagen will ist dass beide Variablen equal1 und equal2 das gleiche ausdrücken wenn a und b != NaN sind, aber nicht im Fall dass wenn a == NaN und b == NaN ist.

    Aus diesem Grunde bin ich ein Freund von Exceptions, welche bei einer Division durch Null direkt eine Exception werfen und so darauf hinweist dass das Ergebnis vermutlich kompletter Müll ist. Die Begründung hast du oben. Zwei mögliche Vergleichsoperatoren welche nur im Fall a=NaN, b=NaN unterschiedliche Ergebnisse liefern. Deswegen ist für mich die Frage ob NaN == NaN ist Unfug.

    Übrigens als Diff Wert kommt bei mir -1.#IND000000 heraus. Jetzt könnte man natürlich auch fragen ob -1.#IND000000 == Nan ist. :p



  • Bitte ein Bit schrieb:

    Tim schrieb:

    Errrrm, nein. a ist inf, b ist inf. inf == inf gilt. Diff ist NaN weil Inf - Inf.

    Das ist mir durchaus klar. Was ich bloß damit sagen will ist dass beide Variablen equal1 und equal2 das gleiche ausdrücken wenn a und b != NaN sind, aber nicht im Fall dass wenn a == NaN und b == NaN ist.

    für a und b NaN kommt doch beidesmal false raus. Ich versteh nicht was du zeigen willst. Zumal fabs(a-b)==0 nicht das gleiche ist, wie a==b . FP ist keine Mathematik mit reellen Zahlen!

    Und NaN == NaN ist sehr wohl definiert (eben false).



  • rüdiger schrieb:

    für a und b NaN kommt doch beidesmal false raus. Ich versteh nicht was du zeigen willst.

    Du meinst wohl equal1 und equal2. Die sind bei mir unterschiedlich denn bei meinem System scheint wohl Nan == Nan zu gelten, trotz Definition.

    Zumal fabs(a-b)==0 nicht das gleiche ist, wie a==b. FP ist keine Mathematik mit reellen Zahlen!

    Thema verfehlt. Du kannst von mir aus gerne den Wertebereich von a und b auf 0.0,1.0,NaN festlegen und bei mir wäre immer noch equal1 und equal2 unterschiedlich.

    Generell verstehe ich nicht warum man mit Nan Werten weiterrechnen will bzw. Vergleiche mit solchen Werten machen will. Ein IsNan() finde ich noch in Ordnung aber ansonsten nichts mehr.



  • Bitte ein Bit schrieb:

    rüdiger schrieb:

    für a und b NaN kommt doch beidesmal false raus. Ich versteh nicht was du zeigen willst.

    Du meinst wohl equal1 und equal2. Die sind bei mir unterschiedlich denn bei meinem System scheint wohl Nan == Nan zu gelten, trotz Definition.

    Bei deinem Beispiel ist a==b natürlich true und fabs(a-b)<0.0 false. Was aber (wie Tim bereits gesagt hat) daran liegt, dass eben a und b nicht NaN sondern eben Inf sind.

    Bitte ein Bit schrieb:

    Generell verstehe ich nicht warum man mit Nan Werten weiterrechnen will bzw. Vergleiche mit solchen Werten machen will. Ein IsNan() finde ich noch in Ordnung aber ansonsten nichts mehr.

    Das würde vorallem bedeuten, dass die IEEE754 Designer ein Exceptionsystem für alle Sprachen hätten festlegen müssen. Es gibt ja die Traps. Aber leider scheinen keine Sprachen native dafür ein System anzubieten. Das man in glibc ein Trap in ein SIGFPE umwandeln kann, ist zwar ok aber auch nicht wirklich hilfreich.

    Der Sinn ist halt, dass man bei einem NaN einfach am Ende schauen kann, ob die Rechnung stimmt oder nicht. Ein Exceptionsystem würde das alles verkomplizieren. W. Kahan führt da zB die Ariane5 an.



  • @rüdinger
    Ich könnte heulen. 😃 Was ist denn jetzt bitte der Unterschied zwischen Nan, Inf (-1.INF) und -1.#IND ??? 😕

    Der Sinn ist halt, dass man bei einem NaN einfach am Ende schauen kann, ob die Rechnung stimmt oder nicht. Ein Exceptionsystem würde das alles verkomplizieren. W. Kahan führt da zB die Ariane5 an.

    Verstehe ich nicht. 😕 Kannst du mir mal einen Link auf den Artikel geben ?



  • Bitte ein Bit schrieb:

    @rüdinger
    Ich könnte heulen. 😃 Was ist denn jetzt bitte der Unterschied zwischen Nan, Inf (-1.INF) und -1.#IND ??? 😕

    http://docs.sun.com/source/806-3568/ncg_goldberg.html

    Bitte ein Bit schrieb:

    Verstehe ich nicht. 😕 Kannst du mir mal einen Link auf den Artikel geben ?

    http://www.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf



  • Danke für die Links 🙂



  • rüdiger schrieb:

    http://docs.sun.com/source/806-3568/ncg_goldberg.html

    What Every Computer Scientist Should Know About Floating-Point Arithmetic

    Oh, das ist nix für mich... 😃


Anmelden zum Antworten