web-dev-qa-db-fra.com

Imprécision de la décimale dans .NET

Hier, lors du débogage, quelque chose d'étrange m'est arrivé et je ne peux pas vraiment l'expliquer:

Decimal calculation

Decimal calculations with brackets

Alors peut-être que je ne vois pas l'évidence ici ou j'ai mal compris quelque chose au sujet des décimales dans .NET, mais les résultats ne devraient-ils pas être les mêmes?

58
10rotator01

decimal n'est pas un type magique faites tout le calcul pour moi type. C'est toujours un nombre à virgule flottante - la principale différence avec float est qu'il s'agit d'un décimal nombre à virgule flottante, plutôt que binaire. Vous pouvez donc facilement représenter 0.3 sous forme décimale (c'est impossible comme nombre binaire fini), mais vous n'avez pas une précision infinie.

Cela le rend beaucoup plus proche d'un humain effectuant les mêmes calculs, mais vous devez toujours imaginer que quelqu'un effectue chaque opération individuellement. Il est spécialement conçu pour les calculs financiers, où vous ne faites pas le genre de choses que vous faites en mathématiques - vous allez simplement étape par étape, en arrondissant chaque résultat selon des règles assez spécifiques.

En fait, dans de nombreux cas, decimal peut fonctionner bien pire que float (ou mieux, double). En effet, decimal ne fait aucun arrondi automatique. Faire la même chose avec double vous donne 22 comme prévu, car il est automatiquement supposé que la différence n'a pas d'importance - dans decimal, cela fait - c'est l'un des points importants sur decimal. Vous pouvez émuler cela en insérant le manuel Math.Rounds, bien sûr, mais cela n'a pas beaucoup de sens.

69
Luaan

Decimal ne peut stocker que des valeurs exactement représentables en décimal dans sa limite de précision. Ici 22/24 = 0,91666666666666666666666 ... qui a besoin d'une précision infinie ou d'un type rationnel pour stocker, et il n'est plus égal à 22/24 après arrondi.

Si vous effectuez d'abord la multiplication, toutes les valeurs sont exactement représentables, d'où le résultat que vous voyez.

30
phuclv

En ajoutant des parenthèses, vous vous assurez que la division est calculée avant la multiplication. Cela semble subtilement suffisant pour affecter suffisamment le calcul pour introduire un problème de précision flottant .

Étant donné que les ordinateurs ne peuvent pas réellement produire tous les nombres possibles, vous devez vous assurer d'en tenir compte dans vos calculs.

14
Sayse

Alors que Decimal a une précision plus élevée que Double, sa principale caractéristique utile est que chaque valeur correspond précisément à sa représentation lisible par l'homme. Bien que les types à virgule fixe qui sont disponibles dans certaines langues puissent garantir que ni l'addition ni la soustraction de deux valeurs à virgule fixe de précision correspondante, ni la multiplication d'un type à virgule fixe par un entier, ne causera jamais d'erreur d'arrondi, et tant que " les types décimaux "tels que ceux trouvés dans Java peut garantir qu'aucune multiplication ne causera jamais d'erreurs d'arrondi, les types Decimal à virgule flottante comme celui que l'on trouve dans .NET n'offre aucun de telles garanties, et aucun type décimal ne peut garantir que les opérations de division peuvent être effectuées sans erreurs d'arrondi (Java's a la possibilité de lever une exception au cas où l'arrondi serait nécessaire).

Bien que ceux qui décident de faire de Decimal un type à virgule flottante aient pu vouloir qu'il soit utilisable soit pour les situations nécessitant plus de chiffres à droite du séparateur décimal ou plus à gauche, les types à virgule flottante, qu'ils soient de base -10 ou base-2, rendent les problèmes d'arrondi inévitables pour toutes les opérations.

1
supercat