Gleitkommazahlen werden intern mit einen Exponenten und einer Mantisse
gespeichet. Die Anzahl der Bits (Signifikanz) der Mantisse ist davon abhängig, ob float oder double gerechnt wird.
0,4 ist als Gleitkommazahl nicht exakt darstellbar, da es keine Potenz von 2 ist.
Deshalb wurde früher auch mit COBOL oder in Assembler programmiert, da gab es Dezimalarithmetik.
PHP-Code:
Beispiel:
Bei einer 32 Bit Gleitkommazahl werden 8 Bits für den Exponenten und
24 Bits für die Mantisse verwendet.
Um die Mantisse mal manuell zu berechnen kann man die
Multiplikationsmethode verwenden,
da ich nicht sooft multiplizieren will bereche ich das Oktal und
überführe das dann in das Binärsystem.
bei der Multiplikationsmethode wird die Zahl mit der Basis des neuen
Zahlensystens multipliziert.
Das gilt aber nur für den Nachkommateil, Stellen vor dem Komma werden mit
Divisionsrestmethodeumgewandelt.
Also:
0,4 * 8
3,2 * 8 die 3 ist die erste Ziffer der Mantisse es wird nur mit dem
1,6 * 8 mit dem Teil hinter dem Komma 0,2 *8 weitergerechnet.
4,8 * 8
6,4 * 8 Hier kommt das Problem, wir haben eine Periode, da wieder
3,2 nur mit 0,4 weiter multiplizert wird.
unsere Mantisse wird somit zu :
3 1 4 6 3 1 4 6 3 1 4 6 ... (oktal)
011 001 100 110 011 001 100 110 011 001 100 110 (binär)
110 011 001 100 110 Das geht in den Bit/Mülleimer
Die Mantisse wird dann normalisiert, d.h. führende nullen werden entfernt
und der Exponent ermittelt (schenke ich mir hier, Stichwort Bias Exponent).
Egal wieviel Stellen die Mantisse hat, es muss es muss abgeschnitten
werden, dadurch ist die Zahl ungenau.
- Warum werden dann machmal aber trotzdem die Zahlen genau
ausgegeben?
Das liegt daran, daß die Ausgaberoutinen schlichtweg manchmal runden,
wenn weniger Stellen ausgegeben werden.
- Wieviel Stellen sind genau?
Das lässt sich leicht aus der Bitanzahl der Mantisse überschlagen.
Für jede Dezimalstelle werden ca 3,3 Bits benötigt.
D.H. bei einer 32 Bit Gleitkommazahl 123456789,4 liegt die
Ungenauigkeit bereits in den Vorkommastellen.
- Man sollte also nie Gleitkommazahlen auf == vergleichen.
if(abs($a - $b) < 0.000005) { print 'fast gleich'; }
wäre eine Möglichkeit. Dabei ist darauf zu achten, das der Schwellwert
nicht ausserhalb der möglichen Genauigkeit der Gleikommazahl liegt.
Ich hoffe , dieser kleine Exkurs in die Grundlagen der Datenverarbeitung
bringt dir das Verständnis für das Problem.
Tröste Dich, Du bist nicht allein in dieser Richtung ahnungslos.
Gruß Papalangi_44 der das vor ca. 40 Jahren gelernt hat.
Wenn ich mal Zeit habe, werden ich mal ne Demo dafür programmieren.