Floats & Doubles..
-
Ich zerbricht mir grad den Kopf über Floats bzw. Doubles. Ich habe damit noch nicht viel Erfahrung, da ich meistens mit ganzzahligen Werten arbeite. Ich muss in einem Programm, das ich am schreiben bin, viele Integerdivisionen durchführen, da ich Distanzen zwischen Punkten in der x,y-Ebene berechnen muss und am Schluss aufaddiere.
Nun folgendes Bsp:
int x1 = 3; int y1 = 5; float z1 = (float)x1/y1; int x2 = 3; int y2 = 5; double z2 = (double)x2/y2;Für z1 bekomme ich 0.60000002 und für z2 0.59999999999999998. Nun wenn ich sehr viele Divisionen ausführe und mit diesen Ungenauigkeiten weiterrechne bekomme ich am Schluss falsche Ergebnisse..
Könnt ihr mir da weiterhelfen?
-
Es gibt da kaum DIE Lösung.
Eine Möglichkeit wäre, das ganze nur mit Brüchen zu berechnen (Bruchklasse schreiben). So eine Klasse kann aber auch ein Problem mit der Darstellbarkeit von Zahlen bekommen (auf overflow achten).Andererseits frage ich mich allerdings, wieso du so eine hohe Präzision bei Abständen zwischen Punkten brauchst?
Wenn du tatsächlich nur Brüche (=Ganzzahldivisionen) aufaddierst, sollte das Endergebnis sehr nahe am richtigen sein. Ist es das nicht, hast du wohl eher irgendwo anders einen Fehler.
-
Vielleicht gibt es für das Problem, was du lösen willst, bessere Algorithmen, die weniger anfällig auf Rundungsfehler reagieren. 'Ne Division ist eigentlich nichts Schlimmes im Sinne der Genauigkeit. Beim Addieren kann schon mehr schief gehen. Dort addieren sich die absoluten Fehler und es kommt ggf zur Auslöschung.
Wenn Du nicht um eine Summe vieler Werte rumkommst und die Genauigkeit wichtig ist, wäre vielleicht Kahan's Summationsalgorithmus etwas für dich. In C++ ließe sich das auch so ganz nett implementieren:
class accu { public: accu(double sum=0, double err=0) : sum(sum), err(err) {} accu& operator+=(double x) { x -= err; double t = sum + x; // MIT Rundungsfehlern, err = (t-sum)-x; // müsste sonst eigentlich 0 sein sum = t; return *this; } operator double() const {return sum;} private: double sum, err; }; ::: int main() { accu sum = 0; for (int k=0; k<999; ++k) { sum += sowieso; } }Man kann auch die ganze Zeit mit etwas höherer Genauigkeit rechnen, zum beispiel mit "quad doubles" (siehe QD), oder per MPFR-Bibliothek. Habe ich aber beides noch nicht getestet.
-
blues_solo schrieb:
Für z1 bekomme ich 0.60000002 und für z2 0.59999999999999998.
Das ist jetzt kein Fehler der Division, sondern ein Problem der Darstellung der Zahl 0.6 als Fließkommazahl im Dualsystem.
Du kannst auch gleich
double z2 = 0.6;schreiben. Kommt das Gleiche bei raus.
Du kannst noch long double probieren, wenn dein System das unterstützt.
-
Ich empfehle, das Ergebnis sinnvoll zu runden. Und untersuche, wie groß der Fehler werden kann und überlege, ob das für dich ausreichend ist. Immerhin tritt der Fehler erst bei der 17. signifikanten Stelle auf. Da musst Du schon ganz schön viel rechnen, bevor Du auf einen signifikanten Fehler kommst.
Aber prinzipiell ist es natürlich sehr sinnvoll, sich darüber Gedanken zu machen. Wenn das Ergebnis ist, dass der Fehler klein genug ist, dann ist man doch auf der sicheren Seite.
-
Vielen Dank für eure Antworten! Warum schreibt der Rechner nicht 0.6? 3/5 hat ja keinen Rest.
DirkB schrieb:
Du kannst noch long double probieren, wenn dein System das unterstützt.
Habe ich ausprobiert. Nun treten keine Fehler mehr auf, die vom gewünschten Ergebnis abweichen

-
blues_solo schrieb:
Vielen Dank für eure Antworten! Warum schreibt der Rechner nicht 0.6? 3/5 hat ja keinen Rest.
Klar. 3/5 ist 0 Rest 3. Und der Computer schreibt nicht 0.6, weil die Zahlen im Binärsystem verschoben werden. Warum schreibst du bei 1/3 nicht einfach 0.1 im Dreiersystem? Genau, weil du sonst das Dezimalsystem nutzt.

-
Hehe stimmt. Da hast du recht

-
@blues_solo
Geh hin und lern über die Gleitkommazahlenblues_solo schrieb:
DirkB schrieb:
Du kannst noch long double probieren, wenn dein System das unterstützt.
Habe ich ausprobiert. Nun treten keine Fehler mehr auf, die vom gewünschten Ergebnis abweichen

Na dann ist ja alles gut.
Tip: Die Abweichungen hast du immer noch, nur jetzt sind sie um ein paar Stellen weiter nach rechts gerückt, und werden bei der Ausgabe weggerundet.
-
Beim Addieren kann schon mehr schief gehen. Dort addieren sich die absoluten Fehler und es kommt ggf zur Auslöschung.
Auslöschung sollte eigentlich weniger ein Problem sein, da Distanzen immer >= 0 sind und damit keine Subtraktion auftreten sollte.
-
@hustbaer: Das werde ich

Ja jetzt ist die Abweichung so klein geworden, dass es beim abrunden auf ganze Zahlen keine Fehler mehr gibt.Ich danke euch nochmals für eure Antworten
Gruss blues_solo