Addition mit +=
-
Hallo ihr!
Mich quält da ein Problem (nichts das man nicht lösen kann). Schaut mal:
#include <iostream> int main(void) { double wt = -0.0000096; double ts = 0.002; double st = 0.0020096; wt += st - ts; std::cout << wt << std::endl; return 0; }
Auf dem Papier ist wt = 0. Nichts aufregendes. Ich bekomme jedoch die Ausgabe: -1.999e-19 !
Klar, man kann den Wert auch so berechnen:
wt = wt + st - ts;
Ich würde trotzdem gern wissen warum das schiefgeht und wie genau der Wert zustande kommt...
Gruß
-
Ersetze die Zeile
std::cout << wt << std::endl;
einmal durch die Zeile
std::cout << std::fixed << wt << std::endl;
/edit: Vielleicht noch kurz zur Erklärung: Mit fixed erzwingst du die Ausgabe als Festpunktzahl und mit setprecision(n) setzt du die Anzahl der Dezimalstellen bei der Ausgabe.
Und Vergleiche außerdem:
#include <iostream> using namespace std; int main(void) { double wt = -0.0000096; double ts = 0.002; double st = 0.0020096; // noch ein test: cout << "\n wt - ts + st: " << fixed << setprecision(20) << wt - ts + st << endl; cout << " plain-calc: " << -0.0000096 - 0.002 + 0.0020096 << endl; return 0; }
Was merkst du? Richtig, der Prozessor rechnet irgendwie nicht korrekt. Weiß auch nicht, warum er es mit den Variablen nicht richtig kann, jedoch mit den plain-Werten. (Wahrscheinlich optimiert der Compiler bei den plain-werten geschickt).
Mit dem +=-Operator hat das aber afaik nichts zu tun.
Caipi
-
Caipi schrieb:
Was merkst du? Richtig, der Compiler rechnet irgendwie nicht korrekt.
Wieso der Compiler? Der Compiler übersetzt doch korrekt. Zur Ausführung (Lebenszeit der Variablen) wird er nicht mehr gebraucht.
-
nillable schrieb:
Caipi schrieb:
Was merkst du? Richtig, der Compiler rechnet irgendwie nicht korrekt.
Wieso der Compiler? Der Compiler übersetzt doch korrekt. Zur Ausführung (Lebenszeit der Variablen) wird er nicht mehr gebraucht.
Ja, du hast recht. Ich meine natürlich den Computer (insbesondere den Prozessor und für ganz genaue die ALU)).
Und das trotz 4-maliger Bearbeitung
Danke!
Caipi
-
ebelcrom schrieb:
wie genau der Wert zustande kommt...
Weil float und double keine beliebig genauen Zahlen darstellen können.
Einfach mal nach "double" in Verbindung mit "Genauigkeit" googeln.
-
Weil float und double keine beliebig genauen Zahlen darstellen können.
Damit bin ich einverstanden, aber wir reden hier nicht von Rundungsfehlern die bei der "398sten" Nachkommastelle passieren... Die Zahlen und das Ergebnis sind klein genug um in double aufgenommen und korrekt berechnet zu werden!
Zumindest hat es nichts mit dem += Operator zu tun. Also ich weiß nicht wie es euch geht, aber wenn mir der Rechner wt - ts + st zur Laufzeit nicht korrekt berechnen kann, dann ist das sehr erschreckend!
Gruß
-
dein computer kann nicht einmal 1/10 richtig ohne rundungsfehler rechnen
-
um der sache auf den Grundzugehen solltest du alle Werte einfach in eine Datei speichern, einen Hexeditor nehmen, dich über den verwendeten Gleitkommazahlstandard informieren und dann wirst du feststellen woran der fehler liegt
-
Ein Problem ist auch, dass manche endlichen Fliesskommazahlen des Dezimalsystems im Dualsystem nicht endlich dargestellt werden können. Man sollte deshalb darauf verzichten Endergebnisse mit so hoher Genauigkeit darzustellen. Bei vielen numerischen Algorithmen wird deshalb mit einem sogenannten "epsilon" gearbeitet. Epsilon ist ein kleiner Wert, den man als Ungenauigkeitsfehler erlaubt. Hier ein primitives Beispiel:
#include <iostream> #include <cmath> double genauigkeit(double wert, double epsilon) { double ergebnis = static_cast<int>(wert);//alle nachkommastellen abmachen double schritt = .1;//mit zehntel genauigkeit anfangen while(fabs(wert-ergebnis)>epsilon) { while((wert-ergebnis)>epsilon) ergebnis+=schritt; //wenns nicht genau genug ist, //schritt zurück und schrittweite verfeinern if(fabs(wert-ergebnis)>epsilon) { ergebnis-=schritt; schritt/=10; } } return ergebnis; } int main(void) { double wt = -0.0000096; double ts = 0.002; double st = 0.0020096; wt += st - ts; std::cout << genauigkeit(wt,1e-19) << std::endl; //nach der 19. Nachkommastelle abschneiden std::cout << genauigkeit(wt,1e-18) << std::endl; //nach der 18. Nachkommastelle abschneiden std::cin.get(); }
-
Ein Problem ist auch, dass manche endlichen Fliesskommazahlen des Dezimalsystems im Dualsystem nicht endlich dargestellt werden können
wie z. B. 1/10
falls du "nur" mit rationalen Zahlen genau rechnen willst schreib dir einfach ne Klasse Rational die dann genau rechnet - und nur bei Ausgaben aus dem Bruch einen double macht
-
#include <iostream> using namespace std; int main(void) { int s = 0; // c muss kleiner als 1 sein double c = 0.002; int s_max = 1000; cout<<c<<" entspricht der Dualzahl"<<endl; cout<<"0."; while(!(c == 0) && !(s == s_max)) { s = s + 1; double a = c*2; cout<<int(a); c = a - int(a); } if(s == 1000) cout<<"konte nicht berechnet werden"<<endl; cout<<endl; system("pause"); }