NaN tritt nur manchmal auf



  • Hallo zusammen,

    ich bin neu hier im Forum und neu in der (C++)-Programmierung. Ich habe eine eher grundsätzliche Frage bzgl. des NaN-Fehlers. Ich habe einen code geschrieben, der 2 sets von Vektoren vergleicht und möglichst gut in Übereinstimmung bringt. Führe ich das Programm nun mehrfach hintereinander aus, kommt es manchmal vor, dass das richtige Ergebnis angezeigt wird, manchmal wird allerdings NaN angezeigt. Es scheint also irgendwie random zu sein, ob das Programm das richtige Ergebnis oder NaN liefert. Meine Frage ist, woran so ein Verhalten grundsätzlich liegen kann?

    Viele Grüße,
    Urwald


  • Mod

    Vermutlich an einem Programmierfehler. Die führen gerne zu sogenanntem "undefinierten Verhalten".



  • Auch Fließkomma-Operationen sind deterministisch, obwohl man manchmal einen anderen Eindruck bekommen könnte, wenn man die Ergebnisse identischer Programme auf verscheidenen Plattformen, oder mit unterscheidlichen FPU-Flags vergleicht.

    Generell ist es also nicht normal, dass so etwas auftritt. Man kann aber durchaus Programme schreiben, die sich so verhalten. Daher:

    • Ist der Algorithmus an irgendeiner Stelle von nicht-vorhersagbaren Daten abhängig, die sich bei jedem Ausführen verändern können? Zufallszahlen/Uhrzeit/Benutzereingaben?

    • Ist die Reihenfolge der Rechenschritte stets die selbe? Verwendest du z.B. mehrere Threads/Prozesse oder Daten, die in unbestimmter Reihenfolge übers Netzwerk hereinkommen?

    • Wird zwischen zwei Programmaufrufen noch irgendwas anderes ausgeführt, das z.B. die Flags der FPU-Einheit in einem veränderten Zustand zurücklassen könnte? (weiss nicht ob das noch ein Ding ist bei moderenen Betriebssystemen, es gab aber in der Vergangenheit schon solche Einflüsse, die zu fehlerhaften Fließkomma-Berechnungen führten, wenn man vorher ein bestimmtes anderes Programm ausführte).

    • Zusatz: und natürlich, wie gerade beim Schreiben von SeppJ erwähnt, ist dein Programm korrekt? Wenn es nicht zu umfangreich ist, dann kannst du es hier ja mal posten - oder noch besser: Auf ein Minimalbeispiel reduzieren, bei dem der Fehler auftritt und dann erst posten 😉

    • zweiter Zusatz: Verwendest du Werte aus irgendwelchen Bibliotheken in deiner Berechnung, für die (rekursiv) einer dieser Punkte zutreffen könnte?



  • Da du schreibst, dass du in der C++ Programmierung neu bist und wenn ich raten müsste, würde ich darauf tippen, dass du irgendwo eine Referenz auf einen Float/Double übergibst, dir der dazugehörige Speicherbereich bei der Abarbeitung aber nicht mehr gehört.


  • Mod

    @Finnegan sagte in NaN tritt nur manchmal auf:

    • Zusatz: und natürlich, wie gerade beim Schreiben von SeppJ erwähnt, ist dein Programm korrekt? Wenn es nicht zu umfangreich ist, dann kannst du es hier ja mal posten - oder noch besser: Auf ein Minimalbeispiel reduzieren, bei dem der Fehler auftritt und dann erst posten 😉

    Unter Linux gibt es beispielsweise valgrind, mit dem man automatisiert (und erstaunlich zuverlässig!) danach suchen kann, ob ein Programm zur Laufzeit einen der typischen Fehler dieser Art macht. Das geht dann auch mit großen Programmen.



  • Hallo zusammen,

    danke fuer eure bisherigen Antworten. Ich habe den Fehler immerhin eingrenzen koennen, es scheint mit dem Einlesen aus einer Datei zu tun zu haben.

    #include <iostream>
    #include <ostream>
    #include <istream>
    #include <fstream>
    #include <sstream>
    #include <string>
    #include <cmath>
    
    #include "/opt/eigen/Eigen/Dense"
    #include "/opt/eigen/Eigen/Eigenvalues"
    #include "/opt/eigen/Eigen/Core"
    
    typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Matrix;
    
    using namespace std;
    int main(int argc, char *argv[])
    {
    
      double wertx  = 0.0, werty = 0.0, wertz = 0.0;
      string sym, dummy;
      string natom;
      int natoms = 0;
      
      // ********************************************************************
      // CHECKING THE INPUT FILES AND READING THEM IN            *******
      //*********************************************************************
    
      ifstream input(argv[1]);
      if(!input) 
        {
          cerr << "File could not be found" << endl;
          return EXIT_FAILURE;
        }
    		
      ifstream inputzwei(argv[2]);
      if(!inputzwei) 
        {
          cerr << "File could not be found" << endl;
          return EXIT_FAILURE;
        }
    	
      getline(input,natom);
      stringstream sstr(natom); 
      sstr >> natoms;
      getline(input,dummy);
      
      //cout << natoms << endl;
      
      double x[3][natoms];
      double y[3][natoms];
      string symbx[natoms];
      string symby[natoms];
     
    
      
      for(int i=0;i<natoms; i++)
        {
          input >> sym >> wertx >> werty >> wertz;
          symbx[i] = sym;
          x[0][i] = wertx;
          x[1][i] = werty;
          x[2][i] = wertz;	
        }
    
      //cout << symbx[227] << "  " << x[0][227] << "  " << x[1][227] << "  " << x[2][227] << endl;
    
      getline(inputzwei,natom);
      getline(inputzwei,dummy);
      
      for(int i=0;i<natoms;i++)
        {
          inputzwei >> sym >> wertx >> werty >> wertz;
          symby[i] = sym;
          y[0][i] = wertx;
          y[1][i] = werty;
          y[2][i] = wertz;	
        }
    
      //cout << symby[227] << "  " << y[0][227] << "  " << y[1][227] << "  " << y[2][227] << endl;
    }
    

    Dies ist nur der erste Teil des codes. Damit der code fehlerfrei funktioniert, muss ich mindestens eines der Feldelemente ausgeben (symbx[i] wuerde dabei reichen). Wenn ich die Ausgabe auskommentiere, tritt der Fehler auf.

    Viele Gruesse,
    Urwald


  • Mod

    Da liegt ja direkt der Verdacht sehr nahe, dass deine Einleseversuche nicht das machen, was du denkst. Da du keinerlei Fehlerprüfung machst, würdest du das aber gar nicht bemerken und arbeitest dann fröhlich auf Mülldaten, falls das passiert. Vielleicht suchst du eher so etwas?

    i = 0;
    while(input >> sym >> wertx >> werty >> wertz)
    {
       [...Werte abspeichern...]
      ++i;
    }
    
    if (i!=natoms)
      // Oh je!
    

    Wobei ich es verwunderlich finde, dass die Anzahl der Zeilen vorher eingegeben werden sollte. Sollte die Zahl der Atome, die in der Datei stehen, sich nicht aus dem Inhalt der Datei ergeben? Normalerweise sehen Leseschleifen nämlich eher so aus:

    while(input >> sym >> wertx >> werty >> wertz)
      mein_datencontainer.push_back(Werte(sym, wertx, werty, wertz));
    
    cout << "Es wurden " << mein_datencontainer.size() << " Werte gelesen";
    

    Da wir hier im C++-Forum sind, solltest du auch dringend das Konstrukt in Zeilen 49-52 unterlassen. Arrays variabler Länge sind ein optionales(!) C-Feature, welches nur von ausgewählten Compilern unterstützt wird, und selbst wenn man C machen würde, dann sind die für ganz andere Zwecke gedacht. Arrays aufgrund von Nutzereingaben zu dimensionieren ist auch da absolutes No-Go.

    Schau dir an, wie man in C++ Daten richtig ablegt:
    https://en.cppreference.com/w/cpp/container
    Wenn du absolut noch nie von irgendetwas davon gehört hast, dann wird dir diese Referenz wahrscheinlich wenig helfen. In dem Fall würdest du erst einmal einen richtigen C++-Kurs benötigen. Einen, bei dem der Lehrer weiß, dass Arrays variabler Länge nicht zu C++ gehören.

    Am Ende, wenn alles zusammen kommt, sollte dein Einlesen eher so aussehen:

    class Atom
    {
      // ...
    };
    
    
    vector<Atom> atoms;
    for (Atom atom; input >>atom;)
      atoms.push_back(atom);
    
    cout << "Es wurden " << atoms.size() << " Atome gelesen";
    

    Das ist dann nicht nur robuster gegen Fehler der Art, die du zeigst, sondern lässt sich auch leichter zur Fehlerfindung nutzen, da die Container von C++ optional auch prüfen können, ob sie richtig benutzt werden.



  • @SeppJ sagte in NaN tritt nur manchmal auf:

    Wobei ich es verwunderlich finde, dass die Anzahl der Zeilen vorher eingegeben werden sollte. Sollte die Zahl der Atome, die in der Datei stehen, sich nicht aus dem Inhalt der Datei ergeben? Normalerweise sehen Leseschleifen nämlich eher so aus:

    while(input >> sym >> wertx >> werty >> wertz)
      mein_datencontainer.push_back(Werte(sym, wertx, werty, wertz));
    
    cout << "Es wurden " << mein_datencontainer.size() << " Werte gelesen";
    

    Lieber SeppJ,

    vielen Dank fuer deinen hilfreichen Beitrag. Der Grund, warum ich die Anzahl der Atome einlese, ist, weil diese in meinen output files direkt als erstes Wort ausgegeben wird (natuerlich kein wirklicher Grund sondern eher eine Plausibelmachung). Ich werde die Eingabe mit Hilfe von vectors ueberarbeiten.

    Viele Gruesse,
    Urwald


  • Mod

    Verstehe ich nicht. Meinst du input? Der so aussieht?

    3
    1 2 3 4
    5 6 7 8
    9 0 1 2
    

    Das wäre ja auch sehr in Ordnung, dass man die Anzahl der erwarteten Werte noch einmal redundant abspeichert, besonders wenn die Dateien von Menschenhand bearbeitet werden.



  • Lieber SeppJ,

    sorry, ja ich meinte input (die Inputfiles sind der output eines anderen Programms). Genau, die haben ein Format, wie du es beschreibst (in der zweiten Zeile steht allerdings noch Unfug, den ich ueberspringen muss). Ich melde mich, wenn ich das Programm ueberarbeitet habe.

    Viele Gruesse,
    Urwald


  • Mod

    Offtopic: Du brauchst in Internetforen übrigens nicht so zu schreiben, als ob das hier ein Briefwechsel wäre. Ist es schließlich nicht. Wirkt nur komisch, wenn man doch so schreibt. Denk dir das eher wie ein Gespräch mittels Sofortnachrichten, bloß mit größerem Umfang der Beiträge.


Log in to reply