NaN == NaN?



  • Dravere schrieb:

    Ich finde einfach diese zusätzlich benötigte Funktion einen Unsinn. Wozu eine zusätzlich Funktion, wenn man schon Vergleiche hat?

    wozu noch extra constants wenn man a!=a benutzen kann?


  • Administrator

    rapso schrieb:

    Dravere schrieb:

    Ich finde einfach diese zusätzlich benötigte Funktion einen Unsinn. Wozu eine zusätzlich Funktion, wenn man schon Vergleiche hat?

    wozu noch extra constants wenn man a!=a benutzen kann?

    Weil das wahnsinnig verwirrend ist. Jeder, welcher sowas liest, denkt sich im ersten Moment: "WTF???". Es stört somit den Lesefluss. Womöglich fällt dem Leser am Ende gar nicht auf, dass hier auf NaN geprüft wird, vor allem wenn kein zusätzlicher Kommentar dortsteht.

    Grüssli



  • Ich schätze mal dass die Umfrage eher das Thema aufwerfen sollte wie einzelne Compiler eine Abfrage der Form (Nan == Nan) implementiert haben und nicht ob Nan == Nan ist.

    Aus programmiertechnischer Sicht ist die Frage ob Nan == Nan ist Unfug. Denn ein Nan entsteht nur durch einen Rechenfehler und das Ergebnis ist ein Fehlerflag genannt Nan. Bei einem Vergleich von a=Nan,b=Nan wären viele Implementationen denkbar. Ein Compiler könnte bei einer Abfrage a==b einfach ein Vergleich der Speicherinhalte machen. Das Ergebnis wäre true. Ein andere Compiler würde hier vielleicht sogar ein Fehlermeldung der Form "Unhandeld Exception in ..." ausgeben. Ein anderer Compiler könnte auch den Rückgabewert ebenfalls auf Nan setzen. Denkbar wäre alles.

    Mit dem folgenden Code bringe ich es sogar fertig dass einmal Nan==Nan true ist und einmal false ist.

    double a = 1;
    double b = 2;
    double Diff;
    bool equal1;
    bool equal2;
    
    a /= 0.0;
    b /= 0.0;
    
    Diff = (a - b);
    equal1 = (fabs(Diff) < 0.1); // oder == 0.0 für exakten Vergleich
    equal2 = (a == b);
    

    Alles nur eine Frage wie a == b implementiert ist.

    Also definiere ich:
    Nan == Nan ist undefiniert 😃



  • FreakY<3Cpp schrieb:

    Für mich ist NaN == NaN -> true. Ich finde ihr denkt viel zu logisch und bringt Beispiele, die damit nichts zu tuen haben. Gefragt ist einfach, Not a Number == Not a Number und das ist für mich true. Ihr setzt wiederrum Sachen für Not a Number ein. Keine Nummer ist keine Nummer.
    Meine Meinung nunmal

    Aehm, allein vom Sprachgebrauch ist das quatsch. Ein Auto ist auch keine Nummer, also "Not a Number". Ein Telefon ist auch keine Nummer, also "Not a Number". Nun soll Telefon == Auto sein? Not a Number schliesst nur aus, dass es keine Nummer ist. Sprachlich stoesst man also auf Inkonsistenten.

    Wieso spezielle extra Funktionen einführen

    Normalerweise sind zwei Elemente x und y genau dann gleich, wenn x <= y und y <= x gilt. Nun kann man sich z.B. fragen, was denn 5 <= NaN ist? Ich vergleiche eine Nummer mit Not a Number. Logisch stoesst man also auf Inkonsistenten.



  • Bitte ein Bit schrieb:

    Mit dem folgenden Code bringe ich es sogar fertig dass einmal Nan==Nan true ist und einmal false ist.

    double a = 1;
    double b = 2;
    double Diff;
    bool equal1;
    bool equal2;
    
    a /= 0.0;
    b /= 0.0;
    
    Diff = (a - b);
    equal1 = (fabs(Diff) < 0.1); // oder == 0.0 für exakten Vergleich
    equal2 = (a == b);
    

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



  • Mit op== vergleicht man die Werte zweier Variablen und/oder Konstanten. NaN ist aber die Darstellung von "hat keinen Wert". => nicht vergleichbar, kann also nicht true sein. Eigentlich genausowenig false, aber es ist das kleinere Übel. Vorbedingung für op== sollte daher sein, dass keiner von beiden Werten NaN ist.
    Nur wie überprüft man das? mit (x != NaN). Das sollte true geben, es sei denn x IST NaN.
    Ergibt:
    NaN != NaN -> false
    NaN == NaN -> false

    Klingt komisch, ist aber so (sollte so sein imo)...



  • pumuckl schrieb:

    Mit op== vergleicht man die Werte zweier Variablen und/oder Konstanten. NaN ist aber die Darstellung von "hat keinen Wert". => nicht vergleichbar, kann also nicht true sein. Eigentlich genausowenig false, aber es ist das kleinere Übel. Vorbedingung für op== sollte daher sein, dass keiner von beiden Werten NaN ist.

    NaN ist sehr wohl ein Wert, nur eben keine Zahl. In C++ muss man solche Bockssprünge machen. In C hat man's leicht: == vergleicht zwei Zahlen. Bei Dingen, die keine Zahl sind, darf es ruhig anders reagieren.
    🙂

    Edit: Grammatik



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



  • 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?
    NaN kann man nicht sortieren, also einfach remove_if und gut ist.



  • Shade Of Mine schrieb:

    Wo würdest du denn NaN hinschieben?

    Ist NaN kleiner als 7? Oder größer als -5?
    NaN kann man nicht sortieren, also einfach remove_if und gut ist.

    Natürlich kann man NaN sortieren, man muss halt ein Kriterium definieren. Z.B. Matlab macht das imho richtige und sortiert alle NaN ans Ende.



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



  • Tim schrieb:

    Natürlich kann man NaN sortieren, man muss halt ein Kriterium definieren. Z.B. Matlab macht das imho richtige und sortiert alle NaN ans Ende.

    klar, das geht natürlich auch. je nach situation ist ein nan aber ein fehler und sollte explizit behandelt werden. aber ja, mein punkt war eher, dass christoph meinte sort ginge nicht und ich wollte ihm zeigen dass sort logisch auch bei (nan==nan)==true nicht gehen würde. (es sei denn man definiert sich eigene rahmenbedinungen)



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


Anmelden zum Antworten