L'opération décimale de SQL Server n'est pas précise

Oct 27 2020

Lorsque j'exécute cette opération simple sur le serveur SQL:

Select 800.0 /30.0 

J'obtiens la valeur 26,666666, où même si elle arrondit à 6 chiffres, elle devrait être 26,666667. Comment puis-je obtenir un calcul précis? J'ai essayé de faire une recherche à ce sujet en ligne et j'ai trouvé une solution où j'ai converti chaque opérande en une décimale de haute précision avant l'opération, mais ce ne sera pas pratique pour moi car j'ai beaucoup de calculs longs et complexes. pense qu'il doit y avoir une meilleure solution.

Réponses

2 Larnu Oct 26 2020 at 23:46

Lors d'une division utilisant, dans SQL Server, tous les chiffres après l'échelle résultante sont tronqués et non arrondis. Pour votre expression, vous avez un decimal(4,1)et un decimal(3,1), ce qui donne un decimal(10,6):

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

En conséquence, 26.66666666666666~est tronqué à 26.666666.

Vous pouvez contourner ce problème en augmentant la taille de la précision et de l'échelle, puis CONVERTrevenir à la précision et à l'échelle requises. Par exemple, augmentez la précision et l'échelle du decimal(3,1)vers decimal(5,2)et reconvertissez-le en decimal(10,6):

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

Cela revient 26.666667.

CR241 Oct 26 2020 at 23:50

Cela peut être utile:

Utiliser ROUND (Transact-SQL)

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

Résultat:

RoundValue

26.666670
seanb Oct 27 2020 at 08:41

Je crois que c'est parce que SQL Server prend vos nombres comme des decimalvaleurs (qui sont exactes, par exemple, 6.6666 et 6.6667 signifient exactement ces valeurs, pas 6 et deux tiers) plutôt que des floatvaleurs (qui peuvent fonctionner avec des nombres approximatifs).

Si vous le lancez / le convertissez explicitement en un floatau début, vous devriez faire fonctionner vos calculs correctement.

Voici quelques exemples pour démontrer la différence entre int, decimalet floatcalculs

  • Diviser 20 par 3
  • Diviser 20 par 3, puis multiplier à nouveau par 3 (ce qui devrait être mathématiquement égal à 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

avec les résultats suivants

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

Dans votre cas, vous pouvez utiliser

Select CAST(800.0 AS float) /30.0

ce qui donne 26,6666666666667

Notez que si vous multipliez ensuite par 30, le résultat est correct, par exemple,

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

donne 800. Les solutions traitant des nombres décimaux n'auront pas cela.

Notez également qu'une fois que vous l'avez en tant que flottant, il devrait rester un flottant jusqu'à ce qu'il soit reconverti en décimal ou en int d'une manière ou d'une autre (par exemple, enregistré dans une table en tant qu'int). Alors...

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

résultera toujours en 26,6666666666667

J'espère que cela vous aidera dans vos longs calculs complexes.