select POWER(2.,64.)
kehrt zurück 18446744073709552000
statt 18446744073709551616
. Es scheint nur 16 Stellen Genauigkeit zu haben (Rundung des 17.).
Selbst wenn die Genauigkeit explizit angegeben wird select power(cast(2 as numeric(38,0)),cast(64 as numeric(38,0)))
, wird das gerundete Ergebnis zurückgegeben.
Dies scheint eine ziemlich grundlegende Operation zu sein, die willkürlich mit einer Genauigkeit von 16 Stellen abbricht. Das Höchste, das es richtig berechnen kann, ist nur POWER(2.,56.)
, wenn es fehlschlägt POWER(2.,57.)
. Was geht hier vor sich?
Was wirklich schrecklich ist, ist, dass 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.;
tatsächlich der richtige Wert zurückgegeben wird. Soviel zur Knappheit.
quelle
Antworten:
Aus der Online-Dokumentation :
Die Implikation ist, dass alles, was Sie als ersten Parameter übergeben, implizit in a umgewandelt wird,
float(53)
bevor die Funktion ausgeführt wird. Dies ist jedoch nicht (immer?) Der Fall .Wenn dies der Fall wäre, würde dies den Präzisionsverlust erklären:
Auf der anderen Seite ist das Literal
2.
Typnumeric
…:dbfiddle hier
… Und der Multiplikationsoperator gibt den Datentyp des Arguments mit der höheren Priorität zurück .
Es scheint, dass im Jahr 2016 (SP1) die gesamte Präzision erhalten bleibt:
dbfiddle hier
… Aber im Jahr 2014 (SP2) sind sie nicht:
dbfiddle hier
quelle
POWER(2.,56.) = 72057594037927936
aber kein höheres. Ich denke, ich muss meine eigene POWER-Funktion schreiben, die sich einfach in einer Schleife multipliziert, lol.Das Ergebnis von 2 64 ist in
float
(undreal
für diesen Fall) genau darstellbar .Das Problem tritt auf, wenn dieses genaue Ergebnis zurück in
numeric
(den Typ des erstenPOWER
Operanden) konvertiert wird .Vor Einführung der Datenbankkompatibilitätsstufe 130 wurde SQL Server
float
aufnumeric
implizite Konvertierungen mit maximal 17 Stellen gerundet .Unter der Kompatibilitätsstufe 130 bleibt bei der Konvertierung so viel Präzision wie möglich erhalten. Dies ist im Knowledge Base-Artikel dokumentiert:
SQL Server 2016-Verbesserungen bei der Behandlung einiger Datentypen und ungewöhnlicher Vorgänge
Um dies in der Azure SQL-Datenbank zu nutzen, müssen Sie den Wert
COMPATIBILITY_LEVEL
auf 130 festlegen :Workload-Tests sind erforderlich, da die neue Regelung kein Allheilmittel darstellt. Beispielsweise:
... sollte einen Fehler auslösen, da 10 38 nicht gespeichert werden kann
numeric
(maximale Genauigkeit von 38). Ein Überlauffehler führt zu einer Kompatibilität unter 120, aber das Ergebnis unter 130 ist:quelle
Mit ein wenig Mathe können wir einen Workaround finden. Für ungerade
n
:Für noch
n
:Eine Möglichkeit, das in T-SQL zu schreiben:
Getestet unter SQL Server 2008 lautet das Ergebnis 144115188075855872 anstelle von 144115188075855870.
Dies funktioniert bis zu einem Exponenten von 113. Es sieht so aus, als ob eine NUMERISCHE Zahl (38,0) bis zu 2 ^ 126 speichern kann, sodass keine vollständige Abdeckung vorliegt. Die Formel kann jedoch bei Bedarf in mehrere Teile aufgeteilt werden .
quelle
Nur zum Spaß eine rekursive CTE-Lösung:
quelle