AW: Ich glaube, wir kommen der Sache näher...
16.05.2024 12:32:07
Der Steuerfuzzi
Das Thema ist wirklich sehr spannend.
Mir geht die interne binäre Darstellung von Gleitkommazahlen nicht aus dem Kopf. Denn nur weil der Bruch 1/3 im Dezimalsystem nicht exakt dargestellt werden kann, heißt das nicht, dass es in Binärform genauso ist. Ich weiß, dass 0,0001 in binär nicht exakt dargestellt werden kann. Ich habe das mal in VBA getestet:
Sub test()
Dim Summe
Dim i As Integer
'Berechnung 1/3
Summe = 0
For i = 1 To 3
Summe = Summe + 1 / 3
Next
Debug.Print Summe
'Berechnung 0.0001
Summe = 0
For i = 1 To 10000
Summe = Summe + 1 / 10000
Next
Debug.Print Summe
End Sub
Ausgabe:
1
0,999999999999906
Hier gibt es bei 3 * 1/3 keine Differenz. Daher erscheint mir das Verhalten doch recht merkwürdig.
Bei meinen ersten Versuchen ist herausgekommen, dass bei Übernahme der Zahl aus der Beispieldatei als Double der Wert als kleiner 2 dargestellt wurde und bei Single als genau 2. Daher habe ich die folgende Summenformel in eine Zelle geschrieben:
=SUMME(1/3;1/3;1/3;1/2;1/2)
Das ist im Prinzip eigentlich identisch mit der Matrixformel von Boris. Wenn ich den gleichen Test hier mache, wird weder bei Single noch bei Double die Zahl als kleiner 2 interpretiert.
Jetzt habe ich die Formel umgestellt:
=SUMME(1/2;1/2;1/3;1/3;1/3)
Es scheint also mit irgendwelchen Zwischenergebnissen zusammenzuhängen.
Zur Veranschaulichung habe ich mir auch mal die interne Speicherung der Zahl angesehen.
Zuerst die Zahl aus der Beispieldatei von Boris. Im Speicher sieht das ganze so aus:
Double:
Hex: 3F FF FF FF FF FF FF FF Binär: 00111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
Dezimal: 1.9999999999999997779553950749686919152736663818359375
Single:
Hex: 40 00 00 00 Binär: 01000000 00000000 00000000 00000000
Dezimal: 2
Und hier die Variante mit der Formel =SUMME(1/3;1/3;1/3;1/2;1/2), bei der der Fehler nicht auftrat. Hier sieht das ganze so aus:
Hex: 40 00 00 00 00 00 00 00 Binär: 01000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Dezimal: 2
Single:
Hex: 40 00 00 00 Binär: 01000000 00000000 00000000 00000000
Dezimal: 2
Jetzt war ich neugierig. Folgende Funktion in VBA ergibt das gleiche Problem:
Sub test()
Dim Summe1 As Double
Dim Summe2 As Single
Dim i As Integer
Summe1 = 0
Summe2 = 0
For i = 1 To 2
Summe1 = Summe1 + 1 / 2
Summe2 = Summe2 + 1 / 2
Next
For i = 1 To 3
Summe1 = Summe1 + 1 / 3
Summe2 = Summe2 + 1 / 3
Next
Debug.Print Summe1
Debug.Print Summe1 = 2
Debug.Print Summe2
Debug.Print Summe2 = 2
End Sub
Ändert man auch hier die Reihenfolge (also erst die 1/3 und dann die 1/2) wird wieder alles korrekt als 2 ausgegeben.
Es erschloss sich mir nicht, warum die zweimalige Addition von 1/2 zuerst den Fehler verursacht und danach nicht. Daher habe ich die Zahlen mal schrittweise addiert. Da die Summe aus 1/3 + 1/3 + 1/3 sowie 1/2 + 1/2 jeweils unstrittig das identische Ergebnis 1 ergeben haben und das auch binär übereingestimmt hat, bin ich von 1 ausgegangen und habe dann jeweils die fehlenden Werte dazu addiert:
1 + 1/3 ergibt folgendes Zwischenergebnis:
Hex: 3F F5 55 55 55 55 55 55 Binär: 00111111 11110101 01010101 01010101 01010101 01010101 01010101 01010101
Dezimal: 1.3333333333333332593184650249895639717578887939453125
Während 1/3 wie folgt aussieht:
Hex: 3F D5 55 55 55 55 55 55 Binär: 00111111 11010101 01010101 01010101 01010101 01010101 01010101 01010101
Dezimal: 0.333333333333333314829616256247390992939472198486328125
Hier sieht man schon, dass die 16. Nachkommastelle bei beiden Dezimaldarstellungen bereits abweichen. Die weiteren Berechnungen haben dann das Ergebnis oben bestätigt. Anscheinend bewirkt die Addition von 1/3 auf 1 bereits eine Ungenauigkeit aufgrund der internen Gleitkommaberechnungen.
Bei der Addition von 1/2 auf 1 passiert hier nichts, so dass es hier nicht zu dieser Ungenauigkeit kommt.
Das ganze kann man ganz gut auf dieser Seite nachvollziehen:
https://zahlensysteme-rechner.de/ieee-754-addieren/
Wenn man die Zahlen binär eingibt (1 und 1/3) wird beim Rechenweg kurz erläutert, dass nach dem Addieren der 64 bit-Repräsentation zwei Bits weg müssen und die Zahl damit abgerundet wird und das wiederholt sich bei den 2 weiteren Additionen. Somit kommt es zur Differenz/Ungenauigkeit.
Bei der 32-bit Repräsentation kommt es bei den ersten beiden Schritten nicht zum abrunden, sondern jeweils zum aufrunden und beim letzten Schritt zum abrunden. Damit gleicht sich das aus und das Ergebnis ist genau 2.
s scheint also logisch, dass bei der Umwandlung in eine Ganzzahl die Nachkommastellen wegfallen. Damit sind wir wieder bei der Annahme, dass es grundlegend darauf ankommt, wie die Funktion die Eingangsparameter interpretiert.
Dass bei Rest eine "anomalie" besteht, macht ist für mich deshalb logisch, da Rest keine Ganzzahl erwartet, sondern mit Nachkommastellen arbeitet. So ergibt z. B. Rest(1,2;1) = 0,2
Rest(1,999999999999999;1) wäre dann 0,999999999999999 aber Rest rundet anscheinend bei 9 Nachkommastellen auf, denn Rest(0,99999999;1) = 0,99999999 aber Rest(0,999999999;1) = 1
Grüße
Michael