Rundungsproblem durch stof



  • Hallo!
    Ich muss in einem Programm einen string in ein float umwandeln. Dabei tritt ein "Rundungsproblem" auf, die Zahl hat eigentlich 6 Nachkommazahlen, die für spätere Berechnungen auch wichtig sind, nach der Conversion hat sie allerdings nur noch 3. Ich habe schon ziemlich viel im Netz gelesen, allerdings das Problem noch nicht lösen können. Könnt ihr mir dabei helfen?

    Hier mein Code mit dem ich die Conversion teste:

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string zahl = "10.661049";
        float z;
        std::size_t* pos =0;
        z = stof (zahl,pos);
        std::cout << z << std::endl;
    }
    

    Damit kriege ich folgendes Warning:

    conversion.cpp:8:24: warning: zero as null pointer constant [-Wzero-as-null-pointer-constant]
        std::size_t* pos = NULL;
                           ^~~~
                           nullptr
    /Library/Developer/CommandLineTools/usr/lib/clang/10.0.0/include/stddef.h:100:18: note: expanded from macro 'NULL'
    #    define NULL __null
                     ^
    

    und wenn ich das ignoriere folgende Ausgabe:

    10.661

    Wo liegt das Problem? Vielen Dank schonmal.


  • |  Mod

    In erster Linie ist das ein Anzeigeproblem:

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string zahl = "10.661049";
        float z;
        z = stof (zahl);
        std::cout.precision(8);
        std::cout << z << std::endl;
    }
    

    Aber: Wenn es wirklich auf solche Präzision ankommt, dann ist float hart am Limit. float hat so 7-8 Stellen Genauigkeit. Da sollte man vielleicht etwas größeres nehmen.


  • Administrator

    Siehe die Antwort von SeppJ bezüglich deiner Frage. Die Warnung hat mit deinem Problem nichts zu tun. Aber um diese auch noch kurz zu erklären. Wenn du einen Zeiger auf Null setzen möchtest, dann verwende unter C++ das Schlüsselwort nullptr. Dieses ist Typsicher und du läufst weniger Gefahr, dass etwas falsches passiert. In deinem Fall kannst du das aber auch ganz weglassen, da das zweite Argument für stof optional ist.

    https://en.cppreference.com/w/cpp/language/nullptr
    https://en.cppreference.com/w/cpp/string/basic_string/stof



  • Du musst zwischen interner Darstellung und der Ausgabe unterscheiden.
    Sieben signifikante Stellen für ein floatsind schon an der Grenze.
    Und die bleiben auch, unabhängig von der Ausgabe, aber im Bereich der Genauigkeit von float.

    Bei der Ausgabe wird gerundet und daher auch nicht alle Stellen ausgegeben

    Zudem hast du den Parameter pos nicht begriffen.
    Was meinst du, macht das =0in Zeile 8?



  • Auch ansehenswert: Is floating point math broken?



  • @SeppJ vielen dank, da steckte wirklich der fehler!



  • @Swordfish

    Noch viel wichtiger, das Kommutativgesetz Assoziativgesetz der Addition bzw. Multiplikation ist bei Gleitkommazahlen nicht gewährleistet. Wenn man große Mengen Zahlen summiert oder multipliziert und z.B. durch Threading die Reihenfolge verändert unterscheiden sich häufig die Ergebnisse!

    Extrembeispiel

    #include <iostream>
    #include <cstddef>
    
    int main () {
        size_t n = 20000000;
        float f = 0.0;
        float  step = 1.0/n;
    
        // Wichtig!
        // Mathematisch ist step * n = 1,
        // demnach muss (step * n) + 1 = 2 sein.
    
        // Zuerst wird n mal step aufsummiert,
        // und anschließend 1 dazu addiert.
        f = 0.0;
        for (size_t i = 0; i < n; ++i) {
            f += step;
        }
    
        f += 1.0;
    
        std::cout << f << std::endl;
    
        // Hier wird nur die Reihenfolge umgedreht,
        // wegen der numerischen Auslöschung bekommen
        // wir ein anderes Ergebnis.
    
        f = 1.0;
        for (size_t i = 0; i < n; ++i) {
            f += step;
        }
    
        std::cout << f << std::endl;
    
        return EXIT_SUCCESS;
    }
    
    

  • |  Mod

    Assoziativität (von mehr als zwei Zahlen) gilt ebenfalls nicht. Floating Points haben überhaupt ganz andere Rechengesetze als die rationalen oder gar die reellen Zahlen. Aber halt auch wiederum nicht so anders, als dass sie für ihren vorgesehenen Einsatzzweck (reelle Zahlen im computer zu approximieren) nicht geeignet wären. Man muss aber halt wissen, was man tut, wenn es auf numerische Genauigkeit ankommt.



  • @john-0 sagte in Rundungsproblem durch stof:

    Kommutativgesetz der Addition bzw. Multiplikation ist bei Gleitkommazahlen nicht gewährleistet

    Kann es sein, dass du Assoziativität mit Kummutativität verwechselst?

    Dein Beispiel zeigt nicht, dass das KG verletzt ist, da auch auch noch das AG anwendest. Mindestens eines der Gesetze ist verletzt.

    Mit s=step:
    a) ist (((s+s)+s)+...)+1(((s + s) + s) + ...) + 1
    In b) berechnest du aber (((1+s)+s)+s)+...(((1 + s) + s) + s) + ..., was du durch Änderung der Assiziativität bekommst s+(s+(...+(s+1)))s + ( s+(... + (s + 1))) und dann noch durch Kommutativität.
    Wenn du eine Aussage nur über das KG treffen wolltest, müsstest du 1+(((s+s)+s)+...)1 + (((s + s) + s) + ...) mit a) vergleichen.

    Es gilt bei IEEE-754-Floats nämlich in der Tat das Kommutativgesetz, aber nicht das Assoziativgesetz (für * und +).

    @SeppJ Assoziativität (von mehr als zwei Zahlen) gilt ebenfalls nicht.

    Steiche das "ebenfalls".



  • @wob sagte in Rundungsproblem durch stof:

    Kann es sein, dass du Assoziativität mit Kummutativität verwechselst?

    Ups, ja ich habe zuviel an Kommutatoren bei Summen gedacht.


Anmelden zum Antworten