Question

Je l'ai fait quelques essais avec des calculs en virgule flottante pour minimiser la perte de précision. Je suis tombé sur un Phenomen Je veux montrer ici et nous espérons obtenir une explication.

Quand j'écris

print 1.0 / (1.0 / 60.0)

le résultat est

60.0024000960

Quand j'écris la même formule et faire la coulée explicite à float

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

le résultat est

60

Jusqu'à présent, je pensais que les littéraux numériques avec décimales sont automatiquement traités comme des valeurs avec la real précision appropriée. Coulée à <=> montre le même résultat que la coulée à <=>.

  • Y at-il une documentation sur la façon dont SQL Server évalue littéraux numériques?
  • De quel type de données sont les littéraux?
  • Dois-je vraiment de les jeter à obtenir une meilleure précision <=> (qui ressemble à l'ironie pour moi:)?
  • Est-il un moyen plus facile que encombrent mes formules avec des moulages?
Était-ce utile?

La solution

SQL Server utilise le plus petit possible type de données.

Lorsque vous exécutez ce 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')

vous verrez que SQL Server utilise implicitement un NUMERIC (2, 1) type de données.
La division par 60,0 convertit le résultat en NUMERIC (8, 6).
Le calcul final convertit le résultat en NUMERIQUE (17, 10).


Modifier

extraits de livres SQL Server en ligne Type de données Conversion

  

Dans instructions Transact-SQL, une constante   avec un point décimal est automatiquement   converti en une valeur numérique de données,   en utilisant la précision minimale et à l'échelle   nécessaire. Par exemple, la constante   12.345 est convertie en une valeur numérique avec une précision de 5 et   échelle de 3.

Autres conseils

Oui, vous avez souvent de les jeter à flotter obtenir une meilleure précision. Mon avis sur la question:

Pour une meilleure précision jeter avant les calculs décimaux

Je pense qu'il faut comprendre ce qui se passe dans les coulisses pour référence dans des cas similaires.

valeurs numériques littérales avec point décimal à l'exclusion des notations scientifiques représentent le type de données Decimal qui est stockée sous la forme la plus petite possible DECIMAL. citation même que Lieven Keersmaekers de de: https://msdn.microsoft.com/en -nous / bibliothèque / ms191530% 28SQL.90% 29.aspx # _decimal

  

Dans instructions Transact-SQL, une constante avec un point décimal est   automatiquement convertie en une valeur numérique de données, en utilisant le minimum   précision et l'échelle nécessaire. Par exemple, la constante est 12.345   converti en une valeur numérique avec une précision de 5 et une échelle de 3.

Les zéros de suivi sur la droite de la virgule décimale précisent échelle. Les zéros à gauche de la virgule sont ignorés.

Voici quelques exemples:

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

Un autre point à considérer est priorité type de données . Lorsqu'un opérateur combine deux expressions de différents types de données, les règles pour le type de données précisent que la priorité du type de données avec la priorité inférieure est convertie au type de données avec la priorité plus élevée. Et encore un autre point à considérer est que si nous faisons des opérations arithmétiques sur les types décimaux que le type décimal obtenu, à savoir la précision et l'échelle dépendent des deux opérandes et le fonctionnement lui-même. Ceci est décrit dans le document Précision, échelle et longueur.

Alors, une partie de votre expression entre parenthèses

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

en utilisant les règles ci-dessus au sujet de précision et l'échelle des expressions décimales. En plus arrondi ou arrondi à même est utilisé le banquier. Il est important de noter différents pour l'arrondissement décimal et le type de flotteur sont utilisés. Si nous continuons l'expression

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

Ainsi, la partie de l'écart est due à différents arrondis utilisés pour les types décimaux que pour float.

Conformément aux règles ci-dessus, il serait suffisant d'utiliser seulement une fonte à l'intérieur des parenthèses. sera promu tous les autres littérale flotter conformément aux règles Type de données précédence.

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

Et encore une chose importante. Même cette expression flottante ne calcule pas résultat exact. Il est juste pour que l'extrémité avant (SSMS ou autre) arrondit la valeur à (je suppose) une précision de 6 chiffres et puis tronque zéros. Donc, à savoir 1.000001 devient 1.

Simple, non?

Pour écrire une expression flottante constante, essayez d'utiliser la notation scientifique:

select (1.0E0 / (1.0E0 / 60.0E0))

Le résultat est 60.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top