2009-07-02 18 views
9

Realicé algunas pruebas con cálculos de coma flotante para minimizar la pérdida de precisión. Me topé con un fenómeno que quiero mostrar aquí y espero obtener una explicación.SQL Server: Cálculo con literales numéricos

Cuando escribo

print 1.0/(1.0/60.0) 

el resultado es

60.0024000960 

Cuando escribo la misma fórmula y hacer una conversión explícita a float

print cast(1.0 as float)/(cast(1.0 as float)/cast(60.0 as float)) 

el resultado es

60 

Hasta ahora yo pensaba que los literales numéricos con decimales se tratan automáticamente como float valores con la precisión adecuada. La conversión a real muestra el mismo resultado que la conversión a float.

  • ¿Hay alguna documentación sobre cómo SQL Server evalúa literales numéricos?
  • ¿De qué tipo de datos son esos literales?
  • ¿De verdad tengo que echarlos a float obtener una mejor precisión (que suena como una ironía para mí :)?
  • ¿Hay alguna manera más fácil que abarrotar mis fórmulas con moldes?

Respuesta

9

SQL Server utiliza el tipo de datos más pequeño posible.

Cuando se ejecuta este script

SELECT SQL_VARIANT_PROPERTY(1.0, 'BaseType') 
SELECT SQL_VARIANT_PROPERTY(1.0, 'Precision') 
SELECT SQL_VARIANT_PROPERTY(1.0, 'Scale') 
SELECT SQL_VARIANT_PROPERTY(1.0, 'TotalBytes') 

verá que SQL Server utiliza implícitamente un valor numérico (2, 1) tipo de datos.
La división por 60.0 convierte el resultado en NUMERIC (8, 6).
El cálculo final convierte el resultado en NUMERIC (17, 10).


Editar

Tomado de SQL Server Libros en Data Type Conversion

En las instrucciones de Transact-SQL, una constante con un punto decimal es automáticamente convertida en un valor de dato numérico, utilizando la precisión mínima y la escala necesaria. Por ejemplo, la constante 12.345 se convierte en un valor numérico con una precisión de 5 y una escala de 3.

+0

Ah .. Yo no sabía que el método hasta ahora. Muchas gracias :) – VVS

+0

¿De modo que no hay forma de evitar un reparto explícito para obtener el resultado esperado? ¿Hay alguna otra forma de decirle a SQL Server que un literal dado es de tipo (por ejemplo) float? – VVS

+0

@VVS, además de un reparto explícito como ya lo está haciendo, no que yo sepa. SQL Server siempre interpreta el valor como numérico.El problema se puede aliviar un poco si usted mismo agrega precisión a sus valores como 1.000000000000000000000000000000000000. –

4

Sí , que con frecuencia tiene que echarlos a flotar obtener una mejor precisión.Mi opinión sobre ella:

For better precision cast decimals before calculations

+0

El razonamiento en el artículo es defectuoso. Se basa en algunos valores arbitrarios. Sería fácil encontrar un ejemplo de contador a favor del tipo Decimal. Estas afirmaciones indias basadas en un ejemplo empírico son trampas en sí mismas. – f470071

3

creo que se debe entender lo que está pasando detrás de las escenas para referencia futura en casos similares.

valores numéricos literales con punto decimal representan excluyendo notación científica tipo de datos decimal que se almacena como mínimo de tipo decimal posible. Misma cita como Lieven Keersmaekers de entre: https://msdn.microsoft.com/en-us/library/ms191530%28SQL.90%29.aspx#_decimal

En instrucciones Transact-SQL, una constante con un punto decimal se convierte automáticamente en un valor de dato numérico, utilizando la precisión y la escala mínima necesaria. Por ejemplo, la constante de 12,345 es convierte en un valor numérico con una precisión de 5 y una escala de 3.

Los ceros a la derecha del punto decimal especificar escala. Los ceros a la izquierda del punto decimal se ignoran.

Algunos ejemplos:

1.0 -> Decimal(2,1) 
60.0 -> Decimal(3,1) 
1.00 -> Decimal(3,2) 
01.0 -> Decimal (2,1) 

Otro punto a considerar es Data Type precedence. Cuando un operador combina dos expresiones de diferentes tipos de datos, las reglas para el tipo de datos precedencia especifican que el tipo de datos con la prioridad más baja se convierte en el tipo de datos con la prioridad más alta. Y otro punto a considerar es si hacemos operaciones aritméticas sobre tipos de decimales que el tipo decimal resultante, es decir, la precisión y la escala dependen de los dos operandos y operación en sí. Esto se describe en el documento Precision, Scale, and Length.

Por lo tanto, parte de su expresión entre paréntesis

(1.0/60.0) is evaluated to 0.016666 and the resulting type is Decimal (8,6) 

usando encima de las reglas sobre la precisión y la escala de las expresiones decimales. Además, se usa el redondeo o el redondeo del banquero para igualar. Es importante tener en cuenta que se utilizan diferentes redondeos para Decimal y tipo flotante. Si seguimos la expresión

1.0/0.016666 is evaluated to 60.002400096 and the resulting type is Decimal (17,10) 

Así que la parte de la discrepancia se debe a diferentes redondeo que se utiliza para este tipo de decimales que para el flotador.

De acuerdo a las reglas anteriores, sería suficiente utilizar sólo un fundido entre paréntesis. Todos los demás literales serán promocionados para flotar de acuerdo con las reglas de precedencia del tipo de datos.

1.0/(1.0/cast(60.0 as float)) 

Y una cosa más IMPORTANTE. Incluso esta expresión flotante no calcula el resultado exacto. Es solo para que el extremo delantero (SSMS o lo que sea) redondee el valor a (supongo) precisión de 6 dígitos y luego trunca los ceros finales. Así es decir, se convierte en 1.000001 1.

simple, ¿verdad?

Cuestiones relacionadas