La operación decimal de SQL Server no es precisa

Oct 27 2020

Cuando ejecuto esta simple operación en el servidor SQL:

Select 800.0 /30.0 

Obtengo el valor 26.666666, donde incluso si se redondea a 6 dígitos, debería ser 26.666667. ¿Cómo puedo hacer que el cálculo sea exacto? Traté de buscarlo en línea y encontré una solución en la que convertí cada operando a un decimal de alta precisión antes de la operación, pero esto no me resultará conveniente porque tengo muchos cálculos largos y complejos. creo que debe haber una mejor solución.

Respuestas

2 Larnu Oct 26 2020 at 23:46

Cuando se usa una división, en SQL Server, cualquier dígito después de la escala resultante se trunca, no se redondea. Para su expresión tiene a decimal(4,1)y a decimal(3,1), lo que resulta en a decimal(10,6):

Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Scale = max(6, s1 + p2 + 1)

Como resultado, 26.66666666666666~se trunca en 26.666666.

Puede evitar esto aumentando el tamaño de la precisión y la escala, y luego CONVERTvolver a la precisión y escala requeridas. Por ejemplo, aumentar la precisión y la escala de la decimal(3,1)de decimal(5,2)y convertir de nuevo a una decimal(10,6):

SELECT CONVERT(decimal(10,6),800.0 / CONVERT(decimal(5,3),30.0));

Esto vuelve 26.666667.

CR241 Oct 26 2020 at 23:50

Esto podría ser útil:

Usar ROUND (Transact-SQL)

SELECT ROUND(800.0 /30.0, 5) AS RoundValue;

Resultado:

RoundValue

26.666670
seanb Oct 27 2020 at 08:41

Creo que es porque SQL Server toma sus números como decimalvalores (que son exactos, por ejemplo, 6.6666 y 6.6667 significa exactamente esos valores, no 6 y dos tercios) en lugar de floatvalores (que pueden funcionar con números aproximados).

Si lo lanza / convierte explícitamente en a floatal principio, debería hacer que sus cálculos funcionen sin problemas.

He aquí algunos ejemplos para demostrar la diferencia entre int, decimaly floatcálculos

  • Dividiendo 20 entre 3
  • Dividir 20 entre 3 y luego multiplicar por 3 nuevamente (que matemáticamente debería ser 20).
SELECT  (20/3)                             AS int_calc,
        (20/3) * 3                         AS int_calc_x3,
        (CAST(20 AS decimal(10,3)) /3)     AS dec_calc,
        (CAST(20 AS decimal(10,3)) /3) * 3 AS dec_calc_x3,
        (CAST(20 AS float) /3)             AS float_calc,
        (CAST(20 AS float) /3) * 3         AS float_calc_x3

con los siguientes resultados

int_calc   int_calc_x3   dec_calc   dec_calc_x3   float_calc         float_calc_x3
6         18             6.666666   19.999998     6.66666666666667   20

En su caso, puede utilizar

Select CAST(800.0 AS float) /30.0

lo que resulta en 26.6666666666667

Tenga en cuenta que si luego vuelve a multiplicar por 30, obtiene el resultado correcto, por ejemplo,

Select (CAST(800.0 AS float) /30.0) * 30

da como resultado 800. Las soluciones que tratan con decimales no tendrán esto.

Tenga en cuenta también que una vez que lo tenga como flotante, debería permanecer como flotante hasta que se convierta de nuevo a decimal o int de alguna manera (por ejemplo, guardado en una tabla como int). Entonces...

SELECT A.Num / 30
FROM (Select ((CAST(800.0 AS float) /30.0) * 30) AS Num) AS A

todavía resultará en 26.6666666666667

Con suerte, esto le ayudará en sus largos cálculos complejos.