 |
Das Problem Sie denken, Ihr Computer rechnet genau? Dann führen Sie doch einmal folgendes Beispiel in Ihrer bevorzugten Programmiersprache (Perl, PHP, Visual Basic, C...) aus. Eventuell müssen Sie es dafür etwas modifizieren, aber das Prinzip bleibt gleich. Unsere kleine Implementation ist in Perl geschrieben.
if (19.08 + 2.01 == 21.09) { print "OK"; } else { print "Da stimmt etwas nicht!"; }
Leider gibt das Script nicht das aus, was wir eigentlich erwarten. Die Schlussfolgerung: wenn wir 19.08 und 2.01 addieren, ist das Ergebnis ungleich 21.09.
Wie kann das sein? Wer im Kopf nachrechnet, wird feststellen, dass 19.08 plus 2.01 sehr wohl exakt 21.09 ist - nur scheint der Computer das nicht zu wissen.
Die Ursache "Der Computer" ist nicht die eigentliche Ursache des Problems. Es gibt Algorithmen, die das richtige Ergebnis problemlos berechnen können.
Ausgangspunkt des Fehlers ist die Art und Weise, wie die meisten Programme ihre Fließkommazahlen abspeichern, nämlich meist dem Standard IEEE 754 folgend. Das bedeutet, dass die dezimale Zahl 2.01 in eine binäre Darstellung überführt wird. Dabei entsteht eine Periode, also das, was auch dann passiert, wenn man 10 durch 3 teilt. Wir Menschen können bequem mit Brüchen weiterrechnen, um trotzdem exakte Ergebnisse zu bekommen. Computer dagegen rechnen normalerweise numerisch, d.h. es wird nicht der Bruch selbst gespeichert, sondern das Ergebnis der Division von Zähler durch Nenner.
Bei 2.01 sind wir aber mit dem Problem konfrontiert, dass eine Periode abgespeichert werden muss, was unmöglich ist - schließlich bedingt die Periode, dass die Zahl unendlich viele Ziffern hat. Unendlich viele Ziffern passen aber nicht in den Speicher des Computers, die Zahl der dort enthaltenen Bits ist leider endlich. Das bedeutet, dass vor dem Speichern der Zahl gerundet werden muss. Rundungen führen jedoch zwangsläufig zu gewissen Differenzen, die letztendlich auch dafür verantwortlich sind, dass der Computer sich manchmal verrechnet.
Sich potenzierende Fehler In unserem Beispiel ist der Fehler noch vergleichsweise gering. Das intern abgespeicherte Ergebnis wird zumindest nahe an 21.09 liegen. Bitte beachten Sie aber, dass Rundungsfehler sich potenzieren können. Nehmen wir an, wir wollen das Ergebnis unserer Addition quadrieren. Das Problem in Bezug auf die Korrektheit des Endergebnisses ist nun, dass wir einen gerundeten Wert mit sich selbst multiplizieren, wodurch der Fehler sich schnell potenzieren kann - besonders dann, wenn mehrere Rechenoperationen hintereinander ausgeführt werden.
Wichtig ist: seien Sie sich stets im Klaren darüber, dass Sie nicht mit exakten Ergebnissen rechnen können, wenn Sie mit Kommazahlen rechnen. Unterschätzen Sie auch nicht, wie schnell sich der Rundungsfehler vergrößern kann, wenn Sie mit gerundeten Werten neue Rechenoperationen ausführen.
Die Reichweite des Problems Die Reichweite dieses Problems ist enorm: stellen Sie sich vor, Sie müssen eine Software für eine Bank schreiben, die Beträge addiert. Hier kann es sehr schnell zu katastrophalen Fehlern und Problemen kommen, wenn z.B. einer der Kunden der Bank auf einmal zu wenig Geld auf seinem Konto hat.
Lösungsansatz 1: Integer verwenden Vermeiden Sie die Verwendung von Fließkommazahlen, wann immer es geht. Oft lassen sich stattdessen Integer einsetzen. Das Integer-Speicherformat unterstützt keine gebrochenen Zahlen, erkauft sich dadurch aber einige Vorteile in Sachen Geschwindigkeit und Genauigkeit. Solange Sie nicht multiplizieren, addieren oder extrem große/kleine Werte verwenden, treten keine Rundungsfehler auf.
Um auf das obige Beispiel zurückzukommen: wenn Sie Cent-Beträge anstelle von Euro addieren, können Sie auf Fließkommazahlen verzichten und Integer einsetzen. Fast alle Programmiersprachen kennen Möglichkeiten, die Verwendung von Integern zu erzwingen. Folgendes Beispiel zeigt eine Implementation in Perl:
use integer; if (1908 + 201 == 2109) { print "OK"; } else { print "Da stimmt etwas nicht!"; }
Lösungsansatz 2: Epsilontik Epsilontik ist ein ironischer Name für die Methodik, alles unterhalb einem bestimmten Wert einfach als 0 anzusehen. Dieser bestimmte Wert wird oft als "Epsilon" bezeichnet, daher auch der Name "Epsilontik".
Im Endeffekt läuft die Epsilontik also darauf heraus, eine gewisse Ungenauigkeit zu akzeptieren. Wenn wir unsere Gleichungen umstellen, kommen wir so zu korrekten Ergebnissen. Folgendes Beispiel wird das verdeutlichen:
print "Ungenau\n" unless (19.08 + 2.01 == 21.09);
use constant epsilon => 1e-14;
print "Mit epsilon richtiges Ergebnis" if ((21.09 - abs(19.08+2.01)) < epsilon);
Der Ansatz ist recht simpel: wenn zwei Werte a und b gleich sind, muss gelten a - b = 0. Da jedoch nicht genau gerechnet wird, gilt hier a - b < epsilon.
Lösungsansatz 3: algebraisches Rechnen Wem es wirklich auf Genauigkeit ankommt, hat immer noch die Möglichkeit, algebraisch (also mit Brüchen, daher genau) zu rechnen. Für viele Programmiersprachen gibt es eine passende Implementation, so z.B. das Math::Fraction-Modul von Perl, das im CPAN erhältlich ist. Folgendes Code-Beispiel zeigt etwas Bruch-Rechnung. Damit das funktioniert, muss das Modul schon eingebunden sein.
# 2/3 + 4/5 $f1=frac(2,3); $f2=frac(4,5); print $f1 + $f2 22/15
Quellen Nicht ganz einfach zu verstehen, aber trotzdem interessant:
[1] J. Orwant, J. Hietaniemi, J. Macdonald, "Algorithmen mit Perl", O`Reilly Verlag, 2000, S. 491f [2] T. Phoenix, Unreal Numbers, http://guest:guest@www.foo.be/docs/tpj/issues/vol2_4/tpj0204-0001.html; zuletzt überprüft am 11.02.2002 [3] G. Steurer-Pernsteiner, "Computerarithmetik und numerische Probleme", http://www.htlwrn.ac.at/edvo/matura/97/d5a13/www/progr.htm; zuletzt überprüft am 18.07.2002 [4] H. Hollatz, "Algorithmen, Rechnen und Computer", http://ianum.math.uni-magdeburg.de/svor.pdf; zuletzt überprüft am 12.02.2002 [5] ohne Verfasserangabe, "Representing Data", http://www-ee.eng.hawaii.edu/Courses/EE150/Book/chap1/subsection2.1.2.1.html; zuletzt überprüft am 12.02.2002 [6] D. Goldberg, ""What Every Computer Scientist Should Know About Floating-Point Arithmetic", http://docs.sun.com/htmlcoll/coll.648.2/iso-8859-1/NUMCOMPGD/ncg_goldberg.html; zuletzt überprüft am 15.02.2002 [7] Ohne Verfasserangabe, "IEEE Arithmetic", http://docs.sun.com/htmlcoll/coll.648.2/iso-8859-1/NUMCOMPGD/ncg_math.html; zuletzt überprüft am 21.02.2002 [8] Ohne Verfasserangabe, "Representation of Floating Point Numbers in Representation of Floating Point Numbers in Single Precision Single Precision IEEE 754 Standard IEEE 754 Standard", http://www.ce.rit.edu/labs/EECC250/eecc250-winter99/250-1-27-2000.pdf; zuletzt überprüft am 23.02.2002
|
 |
|
|
Sie wollen diese Seite ausdrucken? Dafür haben wir eine spezielle Druckversion ohne grafische Elemente entwickelt! |
|
|
Einführung in CSS bei HTMLWorld.
|
|