Rundungsfehler beim konvertieren von float nach int?



  • ich hab mir folgende kleine Routine geschrieben die aus floats einen String bestimmter Laenge machen soll, bei dem der Dezimalpunkt entfernt wurde, fuehrende Nullen und Vorzeichen jedoch beibehalten werden:

    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;
    
    string stripDot(float val, unsigned int length = 3)
    {
      std::ostringstream valstr;
      if (val < 0) 
        { valstr << '-'; val *= -1; --length; }
      cerr << "\t1 " << val << endl;
      while (val < 1.0 && length) 
        { 
          valstr << '0'; val *= 10; --length; 
          cerr << "\t2 " << val << endl;
        }
      if (length)
        {
          while (val < pow(10.0, length-1.0)) val *= 10;
          valstr << (int)val; 
          cerr << "\t3 " << val << ' ' << (int)val <<  endl;
        }
      return valstr.str();
    }
    

    die cerr-statements sind zu debug Zwecken eingebaut worden. Folgendes Programm veranschaulicht mein Problem:

    int main()
    {
      cout << endl << stripDot(0.16) << endl; // 016
      cout << endl << stripDot(1.6) << endl;  // 160
      cout << endl << stripDot(16.0) << endl; // 160
    };
    

    in der ersten Zeile kommt aber 015 raus!!!
    der Output des Programms:

    1 0.16
            2 1.6
            3 16 15      //<------
    
    015
            1 1.6
            3 160 160
    
    160
            1 16
            3 160 160
    
    160
            1 1.6
            3 16 16
    
    -16
    

    Es passiert also folgendes beim Aufruf von stripDot(0.16):
    - if statement uebersprungen, da 0.16 > 0
    - while-schleife wird einmal ausgefuehrt, danach val=1.6, length=2
    - naechste while schleife: pow() liefert 10, 1.6 < 10, also val *= 10 => val = 16.0 (wird ja auch mit 16 ausgegeben)
    - ABER (int)val ergibt 15 !?!?!

    benutzt wird g++ 3.3.3, aber auch CINT (c++-interpreter) liefert das gleiche.
    Was ist da faul?



  • seh jetzt nicht direkt nen fehler im programm (hab nich so genau hingeguckt ;)), aber du rufst die funktion mit double werten auf. bei der konvertierung nach float kann da auch schon präzision verloren gehen.

    1.0; // double
    1.0f; // float
    


  • float ist nie ganz exakt - und vermutlich wird durch die internen Rundungen aus dem 0.16 ein 0.159999... (setz doch mal die precision von cerr etwas höher, dann sieht su auch die Nackommastellen der Zahlen).



  • jau, ging in der 5ten Nachkommastelle dann irgendwann daneben - also doch auf double umsteigen 🙂



  • Wie CStoll schon sagte, sond Fliesskommawerte technisch ungenau. Aber dafür rasend schnell. Wenn floats genau und schnell wären, würde keiner mehr integer benutzen. Mußt also immer mit ungenauen Werten bei float und double rechnen.



  • Artchi schrieb:

    Wenn floats genau und schnell wären, würde keiner mehr integer benutzen.

    Außer wenn man Ganzzahlen benötigt 😉



  • pumuckl schrieb:

    jau, ging in der 5ten Nachkommastelle dann irgendwann daneben - also doch auf double umsteigen 🙂

    Das würde den Fehler auch nur aufschieben, double kann genausowenig exakte Werte speichern wie float (nur mit dem Unterschied, daß es ein wenig exakter ist).

    *grübelt* wenn du einen Gleitkommawert an den Stream übergibst, wird er korrekt gerundet, wenn du ihn nach int konvertierst und dann übergibst, wird er streng abgerundet - also lass doch mal den Cast aus der Anweisung 'valstr<<(int)val;' weg.



  • Ich habe gerade das gleiche.
    Funktion mit dem Namen
    CString FloatToCString(float dValue)
    soll, wie zu erkennen ist, eine Konvertierung vornehmen.
    Der Übergabewert ist float mit dem Wert 0.7
    Springt der Debugger jetzt in die Funktion rein, ist dValue plötzlich 0.699999.
    Warum ? Was kann man dagegen machen ?
    Ich mein, der Wert der übergeben wird ist float, der Datentyp in der Funktion ist float und sowieso.



  • float speichert seine Werte in Binärdarstellung - und da ist 0.7 eine periodische Zahl. Und die passt nicht ganz in den endlichen Platz, den float bereitstellt, deswegen wird sie auf die entsprechende Größe zusammengestutzt.



  • floating point operationen sind immer verlustbehaftet. wenn man wirklich exakte ergebnisse braucht, dann sollte man mit integer werten arbeiten.



  • schau dir mal iomanip an, da kannst du in streams für fließkommatypen festlegen, wie sie dort gespeichert werden sollen, u.a. kann man da anzahl der Stellen,oder anzahl der nachkommastellen festlegen. Da muss man sich eigentlich nichts mehr selberschreiben.



  • Krux schrieb:

    ... wie sie dort gespeichert werden sollen ...

    gespeichert ? eher wie sie bei der ausgabe gerundet werden, oder?

    Meep Meep


Anmelden zum Antworten