SQL Server rundet falsch oder doch nicht

By Frank Kalis

Posted on Jun 7, 2007 von in SQL Server

Multipliziert man zwei Zahlen, die in Addition sowohl bei den Vorkomma- als auch bei den Nachkommastellen, den derzeit "maximalen" Datentyp DECIMAL(38,10) überschreitet, kann man unter Umstände eine Überraschung erleben. Das Ergebnis weicht ab von dem, was man vielleicht erwarten würde.

Allerdings nur oberflächlich betrachtet. Es gibt klar definierte Regeln, die man jedoch kennen muß.

Beispiel:

DECLARE @Multiplikant AS DECIMAL(38, 10)
DECLARE @Multiplikator AS DECIMAL(38, 10)
DECLARE @Result AS DECIMAL(38, 10)
SET @Multiplikant = .1235589123
SET @Multiplikator = 0.55456
SET @Result = @Multiplikant * @Multiplikator
SELECT @Multiplikant, @Multiplikator, @Result

Bevor wir das Ergebnis betrachten, erst einmal ein paar Erläuterungen zu diesem Skript. Wir wollen 2 Zahlen miteinander multiplizieren. Dafür haben wir 3 Variablen vom Typ DECIMAL(38,10) deklariert. Als Ergebnis (in Excel auf 10 Nachkommastellen gerundet) würde man 0,0685208304 erwarten. Läßt man nun das Skript laufen, erhält man folgendes Ergebnis:

                                                                                
------------- ------------- -------------
0.1235589123 0.5545600000 0.0685210000

(1 row(s) affected)

Was ist passiert?
Nun, die deklarierten Variablen Multiplikant und Multiplikator sind jeweils vom Typ DECIMAL(38,10). SQL Server "addiert" Vorkomma- und Nachkommastellen. Das Ergebnis der obigen Rechenoperation müßte also vom Typ DECIMAL(76,20) sein. Dies übersteigt jedoch sowohl die Result Variable als auch den maximal zulässigen DECIMAL Datentyp. Das Ergebnis ist nun, daß SQL Server den Nachkommastellenbereich rundet, um zu vermeiden, daß der Integralpart des Ergebnisses abgeschnitten wird. Dies heißt dann in aller Regel, daß nach 6 Nachkommastellen gerundet wird.

Um nun aus dem obigen Skript ein "korrektes" Ergebnis mit einer hinreichenden Anzahl von Nachkommastellen zu erhalten, muß man die verwendeten Datentypen modifizieren, unter Beachtung der SQL Server-spezifischen Regeln für Precision und Scale.

Bei Multiplikation wird Precision nach der Formel p1 + p2 + 1 errechnet. Scale wird s1 + s2 gerechnet.

Auf das Skript angewendet, könnte man dies folgendermaßen umformulieren:

DECLARE @Multiplikant AS DECIMAL(19, 10)
DECLARE @Multiplikator AS DECIMAL(19, 5)
DECLARE @Result AS DECIMAL(38, 10)
SET @Multiplikant = .1235589123
SET @Multiplikator = 0.55456
SET @Result = @Multiplikant * @Multiplikator
SELECT @Multiplikant, @Multiplikator, @Result

------------- -------- -------------
0.1235589123 0.55456 0.0685208304

(1 row(s) affected)

Wie man sieht, gleich das Ergebnis nun dem mit Excel errechneten. Vom tatsächlich "richtigen" Ergebnis von 0,068520830405088 wurde nur ein nicht signifikanter Anteil an Nachkommastellen abgeschnitten.

Fazit: Manchmal ist weniger mehr. Und gerade wenn man meint, durch Datentypen von vermeindlich hoher Genauigkeit ein hohes Maß an Exaktheit zu erreichen, sollte man sich über die Implikationen bewußt sein.

Tags: Tags:
Dieser Eintrag wurde eingetragen von und ist abgelegt unter SQL Server. Tags: , ,

Noch kein Feedback


Formular wird geladen...