Gestern habe ich auf slashdot.org ein Beitrag eines Programmierers gesehen, der sich darüber beklagt hat, dass von ihm verlangt wurde, eine Steuerberechnung mit JavaScript zu entwickeln. Schließlich kennt JavaScript nur Fließkommazahlen und „jeder“ weiß, dass man damit keine kaufmännischen Anwendungen schreiben kann.
Diese Meinung ist weit verbreitet – gerade in diesen Kreisen. Das weckt bei mir den Argwohn, ob im Bereich kaufmännischer Software verstärkt mäßig erfahrene Programmierer unterwegs sind? Und aus solchen Vorurteilen und mangelnden Wissen resultieren dann BCD Arithmetik-Libraries und BCD Befehle in Prozessoren (BCD = binary coded decimal – zwei Dezimalziffern 0-9 und 0-9 in einem Byte).
Das ist totaler Unfug und zeigt nur einen erheblichen Mangel an (mathematischen) Wissen.
Mythos 1: im Binärsystem kann man Zahlen nicht exakt darstellen, im Dezimalsystem schon.
Fast völliger Unsinn. Es wird immer der Wert 0.1 (als Bruch: 1/10) als Beispiel genommen. Dieser Wert ist im Binärsystem tatsächlich nur gerundet darzustellen, da er dort eine unendliche Periode besitzt. Aber solche Zahlen gibt es im Dezimalsystem auch: 1/3. Dieser Wert lässt sich Dezimal auch nur gerundet darstellen. Prinzipiell gilt für (teilerfremde) Brüche der Art p/q, dass sie eine periodische Darstellung haben, wenn q Primfaktoren besitzt, die in der Basis nicht vorkommen. Das Binärsystem hat nur den Primfaktor 2 in der Basis – wirklich nicht üppig. Im Dezimalsystem sind es 2 und 5 – auch nicht wirklich besser. 1/3 – 1/7 – 1/11 – 1/13… verdammt viele Brüche erzeugen in beiden Systemen eine periodische Darstellung.
Das Problem ließe sich hier noch lösen, indem man mit Rationalen Zahlen arbeitet. Das hat aber Grenzen, denn man kann niemanden eine Rechnung schicken, auf der 47/11 Euro gefordert werden. Auf der Rechnung werden also doch nur 4,272727272727272727272727272727… Euro oder gerundet 4,27 Euro auftauchen.
Mythos 2: mit Fließkommazahlen kann man nicht Cent-genau rechnen
Eine double Fließkommazahl (wie sie z.B. von JavaScript verwendet wird) besitzt über 50 Bit für die Mantisse. Das sind mehr als 15 Dezimalstellen. Damit kann man mehr als nur das gesamte Geld dieser Welt auf einen Cent genau darstellen. Man muss lediglich darauf achten, den Fließkommawert bei der Ausgabe zum nächsten Cent hin auf- oder abzurunden.
Wenn die NASA mit Fließkommaberechnungen Satelliten auf Milliarden Kilometer genau steuern kann, dann sollte auch ein mäßig begabter Programmierer in der Lage sein, eine Leberwurst und 200 Gramm Butter abzurechnen. Das ist keine Raketenwissenschaft.
Mythos 3: wenn ich Integer-Werte für Cent-Beträge verwende, habe ich keine Rundungsfehler
Das gilt nur, wenn man sich auf simple Addition, Subtraktion oder Multiplikation beschränkt. Sobald eine Division ins Spiel kommt, ist der Vorteil dahin (ab hier sind es rationale Zahlen). Sieben Prozent Mehrwertsteuer auf mein Buch für 43,21 Euro sind 3,0247 Euro. Natürlich kann ich auch in hundertstel-Cent statt Cent rechnen. Aber das verschiebt das Problem nur und löst es nicht prinzipiell. Sobald ich den Betrag über eine Split-Buchung auf drei Kostenstellen aufteile, ist es mit exakten Werten vorbei.
Mythos 4: in einer Rechnung müssen alle Beträge auf den Cent genau stimmen
Das funktioniert nur, wenn man genügend ungenau hinschaut. Wenn ich auf einer Rechnung die gerundeten Mwst-Beträge zu jeder Rechnungsposition ausweise, wird die Summe der Beträge nur zufällig exakt zum gerundeten Mwst-Betrag der Netto Summe passen. Das Finanzamt weiß das. Jeder vernünftige Kaufmann ebenfalls. Und ein Programmierer sollte das auch wissen.
Fazit: man kann auch kaufmännische Software mit Fließkommazahlen erstellen
Es müssen halt einfach ein paar Rahmenbedingungen beachtet werden. Alle Ausgabewerte sollten auf den nächsten Cent auf- oder abgerundet werden. Sonst passieren so Dinge wie „überweisen Sie 4,272727272727273 Euro auf unser Konto“. Das ist immer für einen Lacher gut, aber genau genommen ist nichts schlimmes passiert. Der Betrag ist unhandlich aber nicht so falsch, dass ein messbarer Schaden entsteht.
Man kann einen Fließkommawert nicht unbesehen als Schleifenzähler verwenden. Dann kann es zu den gefürchteten „one off“ Fehlern kommen, die Schleife läuft einmal zu oft oder zu wenig. Auch hier muss man mit einer geeigneten Skalierung und Rundung arbeiten – dann geht das sehr wohl.
Bei extrem unterschiedlichen Werten kann die Reihenfolge der Berechnung für das Ergebnis wichtig sein: (1.001 * 10 hoch 15 + 0.00000001) – 1.0005 * 10 hoch 15 wird ein anderes Ergebnis liefern als (1.001 * 10 hoch 15 – 1.0005 * 10 hoch 15) + 0.00000001. So etwas kommt in der Physik und in der Mathematik durchaus vor. In kaufmännischen Anwendungen hat man so eine Dynamik eher nicht.