Zeit für eine kleine Geschichte
Es war einmal ein Logarithmus von 16384. Seine Basis war 4.
Delphi-Quelltext
1:
| pow := trunc(ln(16384)/ln(4)); |
Na, wer denkt, dass pow 7 sein wird? (16384 sind 4^7)
Falsch!
Naja gut, wir wissen ja alle, die CPU speichert die Zahl nicht als 7, sondern als 6.999999998 oder so, ab. Und ein Trunc bewirkt dann, dass auf 6 gerundet wird.
Richtig?
Falsch!
7 lässt sich als Gleitkommazahl EXAKT darstellen.
Mantisse 1.11 Exponent 10
= 1.75 * 2^2 = 7.000000000
Warum ist Trunc von 7.00 dann 6?
Jetzt ist das Ergebnis also plötzlich 7. Alles ist genau wie vorher, nur dass ich das Ergebnis des Logarithmus vorher speichere.
Naja, vielleicht ist das Ergebnis der Division oder des Logs gar nicht 7, sondern eben die besagten 6.99999997. Besonders die Logfunktionen sind sehr ungenau.
Falsch.
Das Ergebnis in der FPU ist
in beiden Fällen Exakt
7!
Das bedeutet, Trunc schafft es irgendwie, aus einer "echten" 7, das heißt, 7.0000000000000, eine 6 zu machen. Aber nur manchmal.
Es geht weiter. Jetzt kommt der absolute Oberknaller.
Selbe Situation wie oben, mit Variable. Allerdings habe ich diesmal den Roundingmode explizit auf down gesetzt...
Seht ihr, was ich sehe?
1. FPU berechnet Log. Ergebnis 7.0000 wird in Double-Variable gespeichert.
2. FPU lädt Variable. Immer noch 7.0000.
3. Trunc macht eine 6 draus.
Warum? Welche Magie ist hier am Werk?
Ich bin mit meinen Erklärungsansätzen am Ende. Wer will es versuchen?
Meine Theorie ist also, dass der Rounding Mode Einfluss auf Funktionen wie div oder log hat, aber man davon nichts sieht...
Theorie : Der Delphi Debugger zeigt ungenaue Zahlen. Wenn da steht "Valid 7" könnte es vielleicht eine 6.99999999999999998 sein.
Gegenargument #1: Dann müsste man zumindest in der Variable die exakte Zahl finden können (Im Zweierkomplement).
Lektion: Man sollte sich sehr, sehr,
sehr genau überlegen, ob und wann, man
Trunc,
Round, oder
Ceil, verwendet.
Ich hatte deswegen eine Menge Ärger. Überhaupt muss man mit Fließkommazahlen höllisch aufpassen.
Vielen Dank fürs Lesen