Seltsame Rundungsfehler



  • Hi!

    Ich versuche mich gerade an einem Beispiel, eine double Zahl in Zähler und Nenner umzuwandeln. Beim Versuch, die Zahl so lange mit 10 zu multiplizieren, bis keine Nachkommastelle mehr übrig bleibt, bin ich aber in eine Endlosschleife geraten. Folgendes Minimalbeispiel...

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    int main(void)
    {
    double dbl=1.234, tst1, tst2;
    
        tst1=floor(dbl*1000);
        tst2=dbl*1000;
        cout << tst1 << endl << tst2 << endl;
    
        if (tst1 != tst2)
            cout << "1) Something is wrong!?" << endl;
    
        if (floor(dbl*1000) != dbl*1000)
            cout << "2) Something is wrong!?" << endl;
    }
    

    ...erzeugt bei mir überraschenderweise folgende Ausgabe:

    1234
    1234
    2) Something is wrong!?
    

    Das kann aber nicht sein, oder? Ist an dem Code was falsch? Wie kann es sein, dass ein Unterschied besteht, ob die Werte in doubles gespeichert werden oder ob sie direkt verglichen werden? Vielleicht ist das Phänomen plattformspezifisch, daher:

    $ g++ -v
    Using built-in specs.
    Target: i486-linux-gnu
    Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang
        --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib
        --without-included-gettext --enable-threads=posix --enable-nls
        --with-gxx-include-dir=/usr/include/c++/4.2 --program-suffix=-4.2
        --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc
        --enable-mpfr --disable-libmudflap --enable-targets=all
        --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu
        --target=i486-linux-gnu
    Thread model: posix
    gcc version 4.2.3 (Debian 4.2.3-1)
    $ uname -r -v
    2.6.22-3-686 #1 SMP Sun Feb 10 20:20:49 UTC 2008
    

    Mit einem Debugger hab ich bisher noch nie gearbeitet. 😕



  • paux schrieb:

    Ich versuche mich gerade an einem Beispiel, eine double Zahl in Zähler und Nenner umzuwandeln.

    Dann sollte dieser Beitrag über die Umwandlung einer Gleitkommazahl in einen Bruch weiterhelfen ...

    paux schrieb:

    Das kann aber nicht sein, oder? Ist an dem Code was falsch? Wie kann es sein, dass ein Unterschied besteht, ob die Werte in doubles gespeichert werden oder ob sie direkt verglichen werden?

    doch das macht einen Unterschied. Wenn die berechneten Werte direkt verglichen werden, so befinden sie sich noch in den Registern der CPU. Wenn diese genauer rechnet als die Zahlen im Speicher abgelegt werden - und das sollte sie normalerweise tun - so sind die Zahlen im Speicher nur gerundete Kopien also nicht zwingend identisch.

    Dein Rechner arbeitet übrigens immer im Binärsystem und die (dezimale) Zahl 1.234 ist im Binärsystem nicht exakt darstellbar.

    Gruß
    Werner



  • Teste doch, ob die Differenz in einer Epsilon-Umgebung liegt:

    if (abs(floor(dbl*1000) - dbl*1000) > 0.01)
    cout << "2) Something is wrong!?" << endl;



  • Danke euch beiden!
    Hätte nicht gedacht dass das so nichttrivial sein könnte, sowohl was die Aufgabenstellung betrifft als auch dass man recht schnell tiefe Hardwarekenntnisse braucht. Hätte auch nicht gedacht hier ebenfalls schon Epsilontik zu brauchen.

    lg


Anmelden zum Antworten