Über (un)genaue Float-Operationen
Wie wirkt sich eigentlich eine ungenaue Float-Operation aus? Sind die Fehler vernachlässigbar?
Durch einen Artikel bei SQL Server Central wurde ich mal wieder inspiriert mich intensiver mit diesem Thema zu beschäftigen.
Beispiel
Im folgenden Beispiel wird die Summe von drei Zahlen berechnet, die einmal als 10001 und einmal als 10000 ermittelt wird, obwohl die drei Summanden identisch sind. Die mathematisch korrekte Beispielrechnung sieht also wie folgt aus:
Beispielrechnung 1 | bzw. | Beispielrechnung 2 |
---|---|---|
10000000000020000 -10000000000010000 + 1 |
-10000000000010000 + 10000000000020000 + 1 |
|
================ 10001 |
================ 10001 |
Im folgenden werden diese Werte in verschiedenen Reihenfolgen in eine Tabelle geschrieben und danach die Summe über die Aggregatsfunktion SUM ermittelt. Ausserdem wird die Summe noch manuell ermittelt. Es zeigt sich, dass bei FLOAT-Datentypen die Reihenfolge der Summanden das Ergebnis beeinflusst.
DECLARE @Fliesskomma1 float, @Fliesskomma2 float, @Fliesskomma3 float; DECLARE @BeispielTabelle table ( ID int primary key identity, TabFloatA float, TabFloatB float, TabDeziC decimal(17,0) ); SET @Fliesskomma1 = 10000000000020000; SET @Fliesskomma2 = -10000000000010000; SET @Fliesskomma3 = 1; INSERT INTO @BeispielTabelle SELECT @Fliesskomma1, @Fliesskomma3, @Fliesskomma3 UNION SELECT @Fliesskomma2, @Fliesskomma1, @Fliesskomma1 UNION ALL SELECT @Fliesskomma3, @Fliesskomma2, @Fliesskomma2;
Die Werte in der Tabelle sehen also wie folgt aus:
ID | TabFloatA | TabFloatB | TabDeziC |
---|---|---|---|
1 | -1,000000000001E+16 | 1,000000000002E+16 | 10000000000020000 |
2 | 1,000000000002E+16 | 1 | 1 |
3 | 1 | -1,000000000001E+16 | -10000000000010000 |
Berechnet man jetzt die Summe dieser Werte auf verschiedene Wege, so sind die Ergebnisse unterschiedlich:
SELECT SUM(TabFloatA) as KorrekteSumme, SUM(TabFloatB) as FalscheSumme, Sum(TabDeziC) as DezimalSumme, @Fliesskomma1 + @Fliesskomma2 + @Fliesskomma3 as SumManuell1 , @Fliesskomma3 + @Fliesskomma1 + @Fliesskomma2 as SumManuell2 FROM @BeispielTabelle;
Die Ergebnisse:
KorrekteSumme | FalscheSumme | DezimalSumme | SumManuell1 | SumManuell2 |
---|---|---|---|---|
10001 | 10000 | 10001 | 10001 | 10000 |
Ursachenforschung
Wo liegen jetzt aber die Ursachen dieser Unterschiede und was bedeutet die Aussage, dass Float-Operationen ungefähre numerische Werte sind? Schaut man in der Online-Doku nach, sieht man, dass die Genauigkeit von float-Daten bei 15 Stellen liegt, falls das Feld einfach als Float definiert wird. Verwendet man Float(n) mit den Werten 1 bis 24, so ist die Genauigkeit nur 7 Stellen.
Man könnte jetzt einwenden: Aber das Ergebnis ist doch ziemlich klein und käme mit 5 Stellen aus!
Wenn man sich aber die Zwischenergebnisse anschaut, stellt man fest, dass hier durchaus berechtigt gerundet wurde. Diese Rundungen/Schätzungen passieren nicht immer; so ist es interessant zu sehen, was passiert, wenn man statt 1 mal die Zahlen von 2 bis 9 verwendet.
Beispielrechnung mit Zwischenergebnissen | |
---|---|
1 + 10000000000020000 |
|
================= ist ungefähr 10000000000020000 |
|
10000000000020000 | - 10000000000010000 = 10000 |
Die ersten 15 Stellen sind signifikant und genau! Hier geht also der kleine Operand verloren, da der grosse Operand bereits die verfügbare Stellenanzahl ausnutzt.
Berechnet man aber zuerst die Summe der beiden grossen Operanden, fällt auch die Rechenoperation mit dem kleinen Operanden nicht mehr unter den Tisch.
Beispielrechnung mit grossen Operanden | |
---|---|
10000000000020000 - 10000000000010000 |
|
================= ist nicht nur ungefähr 10000 |
|
10000 | + 1 = 10001 |
Die weitere Berechnung mit der Zahl 1 fällt problemlos in den Bereich der Genauigkeit und das Endergebnis ist dann auch korrekt.
Fazit
Die Berechnungen mit Float sind nur ungefähre Rechnungen und das Endergebniss ist abhängig von der Reihenfolge der Operanden und kann mehrfachen Rundungen unterworfen sein. Die Ergebnisse aus dem Decimal(17) Feld waren immer genau. Falls man also vorher den möglichen Zahlenbereich genau kennt, kann man unter Umständen die Verwendung von Float umgehen.
Print article | This entry was posted by cmu on 30.10.08 at 21:29:52 . Follow any responses to this post through RSS 2.0. |