select POWER(2.,64.)
retorna 18446744073709552000
em vez de 18446744073709551616
. Parece ter apenas 16 dígitos de precisão (arredondando o 17º).
Mesmo tornando a precisão explícita select power(cast(2 as numeric(38,0)),cast(64 as numeric(38,0)))
, ela ainda retorna o resultado arredondado.
Esta parece ser uma operação bastante básica para que ela esteja lascando arbitrariamente em 16 dígitos de precisão como esta. O máximo que ele pode calcular corretamente é apenas POWER(2.,56.)
, falhando por POWER(2.,57.)
. O que está acontecendo aqui?
O que é realmente terrível é que 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.;
realmente retorna o valor correto. Tanto para concisão.
Da documentação online :
A implicação é que tudo o que você passar como o primeiro parâmetro será convertido implicitamente em a
float(53)
antes que a função seja executada. No entanto, este não é (sempre?) o caso .Se fosse o caso, explicaria a perda de precisão:
Por outro lado, o literal
2.
é do tiponumeric
…:dbfiddle aqui
…e o operador de multiplicação retorna o tipo de dados do argumento com a precedência mais alta .
Parece que em 2016 (SP1), toda a precisão é mantida:
dbfiddle aqui
…mas em 2014 (SP2), eles não são:
dbfiddle aqui
O resultado de 2 64 é exatamente representável em
float
(ereal
para esse assunto).O problema surge quando esse resultado preciso é convertido novamente para
numeric
(o tipo do primeiroPOWER
operando).Antes da introdução do nível de compatibilidade de banco de dados 130, o SQL Server arredondava
float
paranumeric
conversões implícitas para um máximo de 17 dígitos.No nível de compatibilidade 130, a maior precisão possível é preservada durante a conversão. Isso está documentado no artigo da Base de Conhecimento:
Melhorias do SQL Server 2016 na manipulação de alguns tipos de dados e operações incomuns
Para aproveitar isso no Banco de Dados SQL do Azure, você precisa definir
COMPATIBILITY_LEVEL
como 130:O teste de carga de trabalho é necessário porque o novo arranjo não é uma panacéia. Por exemplo:
...deve gerar um erro porque 10 38 não pode ser armazenado
numeric
(precisão máxima de 38). Um erro de estouro resulta em compatibilidade de 120, mas o resultado em 130 é:Com um pouco de matemática, podemos encontrar uma solução alternativa. Para ímpar
n
:Para mesmo
n
:Uma maneira de escrever isso em T-SQL:
Testado no SQL Server 2008, o resultado é 144115188075855872 em vez de 144115188075855870.
Isso funciona até um expoente de 113. Parece que um NUMERIC(38,0) pode armazenar até 2 ^ 126, então não há uma cobertura completa, mas a fórmula pode ser dividida em mais partes, se necessário .
Apenas por diversão, uma solução CTE recursiva: