Ü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 1bzw.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.