select POWER(2.,64.)
zwraca 18446744073709552000
zamiast 18446744073709551616
. Wygląda na to, że ma tylko 16 cyfr precyzji (zaokrąglenie 17).
Nawet precyzując precyzję select power(cast(2 as numeric(38,0)),cast(64 as numeric(38,0)))
, nadal zwraca zaokrąglony wynik.
Wydaje się, że jest to dość podstawowa operacja polegająca na tym, że łuszczy się z taką dokładnością przy 16 cyfrach. Najwyższe, jakie może poprawnie obliczyć, to tylko POWER(2.,56.)
nie POWER(2.,57.)
. Co tu się dzieje?
Naprawdę straszne jest to, że select 2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.;
faktycznie zwraca odpowiednią wartość. Tyle o zwięzłości.
źródło
Odpowiedzi:
Z dokumentacji online :
Implikacja jest taka, że cokolwiek przekażesz jako pierwszy parametr, zostanie domyślnie przekazane do
float(53)
przed wykonaniem funkcji. Jednak nie zawsze tak jest .Gdyby tak było, tłumaczyłoby to utratę precyzji:
Z drugiej strony dosłowny
2.
jest typnumeric
…:dbfiddle tutaj
… A operator mnożenia zwraca typ danych argumentu o wyższym priorytecie .
Wygląda na to, że w 2016 r. (SP1) zachowana jest cała precyzja:
dbfiddle tutaj
… Ale w 2014 r. (SP2) nie są to:
dbfiddle tutaj
źródło
POWER(2.,56.) = 72057594037927936
ale nie wyższy. Chyba będę musiał napisać własną funkcję MOCY, która po prostu mnoży się w pętli, lol.Wynik 2 64 jest dokładnie reprezentowalny w
float
(ireal
pod tym względem).Problem powstaje, gdy ten dokładny wynik zostanie ponownie przekonwertowany na
numeric
(typ pierwszegoPOWER
operandu).Przed poziom zgodności bazy danych 130 został wprowadzony, SQL Server zaokrągleniu
float
donumeric
niejawne konwersje do maksymalnie 17 cyfr.Poniżej poziomu zgodności 130 podczas konwersji zachowana jest jak największa precyzja. Jest to udokumentowane w artykule z bazy wiedzy:
Ulepszenia programu SQL Server 2016 w obsłudze niektórych typów danych i nietypowych operacji
Aby skorzystać z tego w bazie danych Azure SQL, musisz ustawić wartość
COMPATIBILITY_LEVEL
130:Testy obciążenia są potrzebne, ponieważ nowe porozumienie nie jest panaceum. Na przykład:
... powinien zgłosić błąd, ponieważ nie można zapisać 10 38
numeric
(maksymalna dokładność 38). Błąd przepełnienia powoduje zgodność poniżej 120, ale wynik poniżej 130 to:źródło
Przy odrobinie matematyki możemy znaleźć obejście. Dla nieparzystych
n
:Nawet
n
:Jednym ze sposobów na napisanie tego w T-SQL:
Testowany na SQL Server 2008, wynik to 144115188075855872 zamiast 144115188075855870.
Działa to aż do wykładnika 113. Wygląda na to, że NUMERYCZNY (38,0) może przechowywać do 2 ^ 126, więc nie jest całkiem pełny zasięg, ale formuła może być podzielona na więcej części, jeśli to konieczne .
źródło
Dla zabawy rekurencyjne rozwiązanie CTE:
źródło