Vous devriez probablement mettre à l'échelle vos valeurs décimales par 100 et représenter toutes les valeurs monétaires en cents entiers. Ceci permet d'éviter les problèmes de logique et d'arithmétique à virgule flottante . Il n'y a pas de type de données décimal en JavaScript - le seul type de données numérique est à virgule flottante. Par conséquent, il est généralement recommandé de traiter l'argent sous forme de 2550
centimes au lieu de 25.50
dollars.
Considérez cela en JavaScript:
var result = 1.0 + 2.0; // (result === 3.0) returns true
Mais:
var result = 0.1 + 0.2; // (result === 0.3) returns false
L'expression 0.1 + 0.2 === 0.3
retourne false
, mais heureusement l'arithmétique entière en virgule flottante est exacte, donc les erreurs de représentation décimale peuvent être évitées en mettant à l'échelle 1 .
Notez que si l'ensemble des nombres réels est infini, seul un nombre fini d'entre eux (18 437 736 874 454 810 627 pour être exact) peut être représenté exactement par le format à virgule flottante JavaScript. Par conséquent, la représentation des autres nombres sera une approximation du nombre réel 2 .
1 Douglas Crockford: JavaScript: Les bonnes parties : Annexe A - Les horribles parties (page 105) .
2 David Flanagan: JavaScript: le guide définitif, quatrième édition : 3.1.3 littéraux à virgule flottante (page 31) .
Daniel Vassallo
la source
3000.57 + 0.11 === 3000.68
revientfalse
.Mettre à l'échelle chaque valeur par 100 est la solution. Le faire à la main est probablement inutile, car vous pouvez trouver des bibliothèques qui le font pour vous. Je recommande moneysafe, qui propose une API fonctionnelle bien adaptée aux applications ES6:
https://github.com/ericelliott/moneysafe
Fonctionne à la fois dans Node.js et dans le navigateur.
la source
in$, $
noms de valeur sont ambigus pour quelqu'un qui n'a jamais utilisé le package auparavant. Je sais que c'était le choix d'Eric de nommer les choses de cette façon, mais je pense toujours que c'est une erreur suffisante pour que je les renomme probablement dans l'instruction require import / destructured.Money$afe has not yet been tested in production at scale.
. Il suffit de le signaler pour que tout le monde puisse ensuite déterminer si cela convient à son cas d'utilisationIl n'y a pas de calcul financier "précis" à cause de seulement deux chiffres de fraction décimale, mais c'est un problème plus général.
En JavaScript, vous pouvez mettre à l'échelle chaque valeur par 100 et l'utiliser à
Math.round()
chaque fois qu'une fraction peut se produire.Vous pouvez utiliser un objet pour stocker les nombres et inclure l'arrondi dans sa
valueOf()
méthode de prototypes . Comme ça:De cette façon, chaque fois que vous utilisez un objet Money, il sera représenté comme arrondi à deux décimales. La valeur non arrondie est toujours accessible via
m.amount
.Vous pouvez intégrer votre propre algorithme d'arrondi dans
Money.prototype.valueOf()
, si vous le souhaitez.la source
Money(0.1)
, cela signifie que le lexer JavaScript lit la chaîne "0,1" à partir de la source, puis la convertit en virgule flottante binaire et que vous avez déjà fait un arrondi involontaire. Le problème concerne la représentation (binaire vs décimal) et non la précision .use decimaljs ... C'est une très bonne bibliothèque qui résout une partie difficile du problème ...
utilisez-le simplement dans toutes vos opérations.
https://github.com/MikeMcl/decimal.js/
la source
Votre problème provient de l'inexactitude des calculs en virgule flottante. Si vous utilisez simplement l'arrondi pour résoudre ce problème, vous aurez une plus grande erreur lorsque vous multipliez et divisez.
La solution est ci-dessous, une explication suit:
Vous devrez penser aux mathématiques derrière cela pour le comprendre. Les nombres réels comme 1/3 ne peuvent pas être représentés en maths avec des valeurs décimales car ils sont infinis (par exemple - .333333333333333 ...). Certains nombres en décimal ne peuvent pas être représentés correctement en binaire. Par exemple, 0,1 ne peut pas être représenté correctement en binaire avec un nombre limité de chiffres.
Pour une description plus détaillée, regardez ici: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Jetez un œil à l'implémentation de la solution: http://floating-point-gui.de/languages/javascript/
la source
En raison de la nature binaire de leur codage, certains nombres décimaux ne peuvent pas être représentés avec une précision parfaite. Par exemple
Si vous avez besoin d'utiliser du javascript pur, vous devez penser à une solution pour chaque calcul. Pour le code ci-dessus, nous pouvons convertir des décimales en entiers entiers.
Éviter les problèmes avec les mathématiques décimales dans JavaScript
Il existe une bibliothèque dédiée aux calculs financiers avec une excellente documentation. Finance.js
la source
Malheureusement, toutes les réponses jusqu'à présent ignorent le fait que toutes les devises n'ont pas 100 sous-unités (par exemple, le cent est la sous-unité du dollar américain (USD)). Des devises comme le dinar irakien (IQD) ont 1000 sous-unités: un dinar irakien a 1000 fils. Le yen japonais (JPY) n'a pas de sous-unités. Donc "multiplier par 100 pour faire de l'arithmétique entière" n'est pas toujours la bonne réponse.
De plus, pour les calculs monétaires, vous devez également garder une trace de la devise. Vous ne pouvez pas ajouter un dollar américain (USD) à une roupie indienne (INR) (sans d'abord convertir l'un en l'autre).
Il existe également des limitations sur la quantité maximale qui peut être représentée par le type de données entier de JavaScript.
Dans les calculs monétaires, vous devez également garder à l'esprit que l'argent a une précision finie (généralement de 0 à 3 décimales) et que l'arrondi doit être fait de manière particulière (par exemple, arrondi «normal» ou arrondi de la banque). Le type d'arrondi à effectuer peut également varier selon la juridiction / la devise.
Comment gérer l'argent en javascript a une très bonne discussion sur les points pertinents.
Dans mes recherches, j'ai trouvé la bibliothèque dinero.js qui traite de nombreux problèmes liés aux calculs monétaires . Je ne l'ai pas encore utilisé dans un système de production, je ne peux donc pas donner une opinion éclairée à ce sujet.
la source