select POWER(2.,64.)
vuelve en 18446744073709552000
lugar de 18446744073709551616
. Parece tener solo 16 dígitos de precisión (redondeando el 17).
Incluso haciendo explícita la precisión select power(cast(2 as numeric(38,0)),cast(64 as numeric(38,0)))
, aún devuelve el resultado redondeado.
Esto parece una operación bastante básica para que se descarte arbitrariamente a 16 dígitos de precisión como esta. Lo más alto que puede calcular correctamente es solo POWER(2.,56.)
, fallando POWER(2.,57.)
. ¿Que esta pasando aqui?
Lo que es realmente terrible es que en 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.;
realidad devuelve el valor correcto. Demasiado para la brevedad.
fuente
Respuestas:
De la documentación en línea :
La implicación es que lo que pase como primer parámetro se convertirá implícitamente en a
float(53)
antes de que se ejecute la función. Sin embargo, este no es (¿siempre?) El caso .Si fuera el caso, explicaría la pérdida de precisión:
Por otro lado, el literal
2.
es tiponumeric
...:dbfiddle aquí
... y el operador de multiplicación devuelve el tipo de datos del argumento con mayor precedencia .
Parece que en 2016 (SP1), se conserva toda la precisión:
dbfiddle aquí
... pero en 2014 (SP2), no son:
dbfiddle aquí
fuente
POWER(2.,56.) = 72057594037927936
pero no superior. Supongo que tendré que escribir mi propia función POWER que simplemente se multiplica en un bucle, jajaja.El resultado de 2 64 es exactamente representable en
float
(yreal
para el caso).El problema surge cuando este resultado preciso se convierte de nuevo a
numeric
(el tipo del primerPOWER
operando).Antes de que se introdujera el nivel de compatibilidad de la base de datos 130, SQL Server redondeó
float
anumeric
conversiones implícitas a un máximo de 17 dígitos.Bajo el nivel de compatibilidad 130, se conserva la mayor precisión posible durante la conversión. Esto está documentado en el artículo de Knowledge Base:
Mejoras de SQL Server 2016 en el manejo de algunos tipos de datos y operaciones poco comunes
Para aprovechar esto en Azure SQL Database, debe establecer
COMPATIBILITY_LEVEL
130 en:La prueba de carga de trabajo es necesaria porque el nuevo arreglo no es una panacea. Por ejemplo:
... debería arrojar un error porque no se puede almacenar 10 38
numeric
(precisión máxima de 38). Un error de desbordamiento da como resultado una compatibilidad inferior a 120, pero el resultado por debajo de 130 es:fuente
Con un poco de matemática podemos encontrar una solución. Por impar
n
:Para incluso
n
:Una forma de escribir eso en T-SQL:
Probado en SQL Server 2008, el resultado es 144115188075855872 en lugar de 144115188075855870.
Esto funciona hasta un exponente de 113. Parece que un NUMÉRICO (38,0) puede almacenar hasta 2 ^ 126, por lo que no hay una cobertura completa, pero la fórmula podría dividirse en más partes si es necesario .
fuente
Solo por diversión, una solución recursiva de CTE:
fuente