Somme non déterministe de flottants


10

Permettez-moi de dire le poing évident: je comprends parfaitement que les types à virgule flottante ne peuvent pas représenter avec précision les valeurs décimales . Ce n'est pas ça! Néanmoins, les calculs en virgule flottante sont censés être déterministes .

Maintenant que cela est hors de propos, laissez-moi vous montrer le cas curieux que j'ai observé aujourd'hui. J'ai une liste de valeurs à virgule flottante, et je veux les résumer:

CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);

SELECT STR(SUM(#someFloats.val), 30, 15) FROM #someFloats;

DROP TABLE #someFloats;

-- yields:
--   13.600000000000001

Jusqu'à présent, tout va bien - pas de surprise ici. Nous savons tous que cela 1.2ne peut pas être représenté exactement en représentation binaire, donc le résultat "imprécis" est attendu.

Maintenant, la chose étrange suivante se produit lorsque je quitte une autre table:

CREATE TABLE #A (a int);
INSERT INTO #A (a) VALUES (1), (2);

CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);

SELECT #A.a, STR(SUM(#someFloats.val), 30, 15)
  FROM #someFloats LEFT JOIN #A ON 1 = 1
 GROUP BY #A.a;

DROP TABLE #someFloats;
DROP TABLE #A;

-- yields
--   1   13.600000000000001
--   2   13.599999999999998

( violon sql , vous pouvez également y voir le plan d'exécution)

J'ai la même somme sur les mêmes valeurs, mais une erreur à virgule flottante différente . Si j'ajoute plus de lignes au tableau #A, nous pouvons voir que la valeur alterne entre ces deux valeurs. Je n'ai pu reproduire ce problème qu'avec un LEFT JOIN; INNER JOINfonctionne comme prévu ici.

Cela n'est pas pratique, car cela signifie que a DISTINCT, GROUP BYou les PIVOTvoit comme des valeurs différentes (c'est en fait ainsi que nous avons découvert ce problème).

La solution évidente est d'arrondir la valeur, mais je suis curieux: y a-t-il une explication logique à ce comportement?

Réponses:


15

En fait, le lien auquel vous faites référence ne dit pas que les calculs arithmétiques à virgule flottante sont toujours déterministes. En fait, dans l'une des réponses, il est mentionné que l'addition n'est pas associative (ce (a + b) + cqui signifie pas nécessairement égal a + (b + c)), ce qui est également dit dans cette réponse .

Si l'agrégation de flux se produit pour traiter les lignes de chaque groupe dans un ordre différent - ce que SQL Server est généralement libre de faire; s'il n'y en a pas ORDER BYdans la clause appropriée, l'optimiseur choisira le scan ou la recherche ou tout autre opérateur de requête le plus rapide, quel que soit l'ordre dans lequel les ajouts sont effectués - cela pourrait expliquer le comportement que vous observez.

L'addition est toujours déterministe: vous mettez les deux mêmes flotteurs, vous sortez le même flotteur. Mais ajouter des flotteurs ensemble dans un ordre différent peut donner un résultat différent.


L'associativité n'a aucun rapport avec le déterminisme, donc ce bit est trompeur.
Mooing Duck

La non-associativité de l'addition en virgule flottante conduit à un comportement non déterministe de la fonction d'agrégation SQL Server SUM(), seriez-vous d'accord avec @MooingDuck?
mustaccio

Non? La division entière est un contre-exemple clair. Il est non associatif, mais entièrement déterministe. De même, la division en virgule flottante doit être non associative et toujours déterministe. De cela, nous concluons qu'il est raisonnable que l'addition soit non associative et toujours déterministe. Cela étant dit, si l'ordre des ajouts n'est pas déterministe, le résultat ne sera pas non plus déterministe, donc votre première et votre dernière phrase sont toujours correctes malgré tout.
Mooing Duck

La division entière est un contre-exemple pour SQL Server SUM()sur des arguments à virgule flottante, comment exactement?
mustaccio

1
La division entière est non associative et déterministe. Par conséquent, l'associativité des opérations arithmétiques n'est pas liée au déterminisme. Par conséquent, toute non-associativité de SUM()doit être sans rapport avec son déterminisme. Je conviens que cela SUMsemble non déterministe, mais vous devriez supprimer les mentions d'associativité, car cela n'a aucun rapport.
Mooing Duck
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.