2009-07-02 10 views
9

Wykonałem kilka testów z obliczeniami zmiennoprzecinkowymi, aby zminimalizować utratę precyzji. Natknąłem się na fenomen, który chcę tutaj pokazać i mam nadzieję, że otrzymam wyjaśnienie.Serwer SQL: obliczanie za pomocą literałów numerycznych

Kiedy piszę

print 1.0/(1.0/60.0) 

wynik jest

60.0024000960 

Kiedy piszę ten sam wzór i zrobić wyraźne odlewania do float

print cast(1.0 as float)/(cast(1.0 as float)/cast(60.0 as float)) 

wynik jest

60 

Do tej pory myślałem, że literały liczbowe z miejsc po przecinku są automatycznie traktowane jako float wartości z odpowiednią precyzją. Przesyłanie do wersji real przedstawia ten sam wynik, co rzutowanie na float.

  • Czy jest jakaś dokumentacja na temat sposobu, w jaki SQL Server ocenia literały numeryczne?
  • Z jakiego typu są te literały?
  • Czy naprawdę muszę rzucić je do float uzyskać lepszą precyzję (co brzmi jak ironia do mnie :)?
  • Czy jest łatwiejszy sposób niż zaśmiecanie moich formuł za pomocą odlewów?

Odpowiedz

9

SQL Server używa najmniejszego możliwego typu danych.

Kiedy uruchomić ten skrypt

SELECT SQL_VARIANT_PROPERTY(1.0, 'BaseType') 
SELECT SQL_VARIANT_PROPERTY(1.0, 'Precision') 
SELECT SQL_VARIANT_PROPERTY(1.0, 'Scale') 
SELECT SQL_VARIANT_PROPERTY(1.0, 'TotalBytes') 

zobaczysz, że SQL Server niejawnie używany numerycznej (2, 1) typ danych.
Podział o 60.0 zamienia wynik na NUMERYCZNY (8, 6).
Ostateczne obliczenie przekształca wynik na NUMERYCZNY (17, 10).


Edit

Zrobione z SQL Server Books Online Data Type Conversion

W Transact-SQL, stałym z przecinkiem jest automatycznie przekształcony liczbową wartość danych, konieczne jest użycie minimalnej precyzji i skali . Na przykład, stała 12,345 przekształca się w wartości liczbowej z dokładnością do 5 oraz skali od 3.

+0

Ah .. Nie znałem tej metody aż do teraz. Bardzo dziękuję :) – VVS

+0

Więc nie ma sposobu na wyraźną obsadę, aby uzyskać oczekiwany rezultat? Czy istnieje inny sposób powiedzenia serwerowi SQL, że dany literał jest typu (np.) Zmiennoprzecinkowy? – VVS

+0

@VVS, oprócz wyraźnej obsady, którą już wykonujesz, o czym nie wiem. SQL Server zawsze interpretuje wartość jako numeryczną.Problem można nieco złagodzić, jeśli dodajesz precyzję do wartości takich jak 1.000000000000000000000000000000000000. –

4

Tak , często mają do oddania im unosić się lepszą precyzję.My się na to:

For better precision cast decimals before calculations

+0

Uzasadnienie artykułu jest wadliwe. Opiera się na pewnych arbitralnych wartościach. Łatwo byłoby znaleźć kontrprzykład na korzyść rodzaju dziesiętnego. Te indyjskie twierdzenia oparte na jednym empirycznym przykładzie to pułapki same w sobie. – f470071

3

myślę, że powinno być rozumiane, co dzieje się za kulisami w przyszłości w podobnych przypadkach.

Dosłowne wartości liczbowe z kropką dziesiętną z wyłączeniem notacji naukowej reprezentują typ danych dziesiętnych, który jest przechowywany jako najmniejszy możliwy typ dziesiętny. Tak samo jak cytat z Lieven Keersmaekers użytkownika: https://msdn.microsoft.com/en-us/library/ms191530%28SQL.90%29.aspx#_decimal

W Transact-SQL, stała z przecinkiem jest automatycznie przekształcona liczbową wartość danych, przy użyciu minimalnej precyzję i skalę niezbędną. Na przykład, stały 12.345 jest przekształcono w wartości liczbowej z dokładnością do 5, a skalę 3.

The zer na prawo od kropki dziesiętnej określić skalę. Przednie z lewej strony kropki dziesiętnej są ignorowane.

Kilka przykładów:

1.0 -> Decimal(2,1) 
60.0 -> Decimal(3,1) 
1.00 -> Decimal(3,2) 
01.0 -> Decimal (2,1) 

Inną kwestią do rozważenia jest Data Type precedence. Gdy operator łączy dwa wyrażenia różnych typów danych, reguły pierwszeństwa typu danych określają, że typ danych o niższym priorytecie jest konwertowany na typ danych o wyższym priorytecie. Jeszcze jeden punkt do rozważenia jest to, czy wykonujemy operacje arytmetyczne na typach dziesiętnych, że wynikowy typ dziesiętny, tj. Precyzja i skala zależą od obu operandów i samej operacji. Jest to opisane w dokumencie Precision, Scale, and Length.

Więc część swojej wypowiedzi w nawiasie

(1.0/60.0) is evaluated to 0.016666 and the resulting type is Decimal (8,6) 

użyciu wyżej przepisów o precyzji i skali wyrażeń po przecinku. Dodatkowo stosuje się zaokrąglanie lub zaokrąglanie bankiera do parzystości. Ważne jest, aby zauważyć, że używane są różne zaokrąglenia dla dziesiętnych i typu zmiennoprzecinkowego. Jeśli nadal ekspresję

1.0/0.016666 is evaluated to 60.002400096 and the resulting type is Decimal (17,10) 

Więc część rozbieżności ze względu na zaokrąglenia inny używany dla typów dziesiętnych niż dla pływaka.

Zgodnie z powyższymi zasadami wystarczy użyć tylko jednego odlewu w nawiasie. Każdy inny literał będzie promowany w celu unoszenia się zgodnie z regułami pierwszeństwa typu danych.

1.0/(1.0/cast(60.0 as float)) 

I jeszcze jedna WAŻNA rzecz. Nawet to wyrażenie float nie oblicza dokładnego wyniku. Jest tak, że front (SSMS lub cokolwiek innego) zaokrągla wartość do (myślę) dokładność 6 cyfr, a następnie obcina końcowe zera. Więc to 1.000001 staje się 1.

Proste, czyż nie?

Powiązane problemy