web-dev-qa-db-fra.com

Calcul financier précis en JavaScript. Quels sont les pièges?

Dans l’intérêt de créer du code multiplate-forme, j'aimerais développer une application financière simple en JavaScript. Les calculs requis impliquent un intérêt composé et des nombres décimaux relativement longs. J'aimerais savoir quelles erreurs éviter en utilisant JavaScript pour faire ce type de calcul - si c'est possible! 

106
james_womack

Vous devriez probablement mettre à l'échelle vos valeurs décimales de 100 et représenter toutes les valeurs monétaires en centimes. Cela évite des problèmes de logique à virgule flottante et d'arithmétique . JavaScript ne contient aucun type de données décimal - le seul type de données numérique est à virgule flottante. Par conséquent, il est généralement recommandé de gérer l'argent en tant que 2550 cents 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 renvoie false, mais heureusement, l'arithmétique des nombres entiers en virgule flottante est exacte. Vous pouvez donc éviter les erreurs de représentation décimales en mettant à l'échelle1.

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 de virgule flottante JavaScript. Par conséquent, la représentation des autres nombres sera une approximation du nombre réel2.


1 Douglas Crockford: JavaScript: Les bonnes parties: Annexe A - Les mauvaises parties (page 105) .
2 David Flanagan: JavaScript: Le Guide définitif, quatrième édition: 3.1.3 Littéraux à virgule flottante (page 31) .

93
Daniel Vassallo

Mettre à l'échelle chaque valeur de 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:

const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8

https://github.com/ericelliott/moneysafe

Fonctionne à la fois dans Node.js et le navigateur.

11

Un calcul financier «précis» n’existe pas à cause de deux chiffres décimaux, mais c’est un problème plus général.

En JavaScript, vous pouvez redimensionner chaque valeur de 100 et 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 méthode de prototypes valueOf(). Comme ça:

sys = require('sys');

var Money = function(amount) {
        this.amount = amount;
    }
Money.prototype.valueOf = function() {
    return Math.round(this.amount*100)/100;
}

var m = new Money(50.42355446);
var n = new Money(30.342141);

sys.puts(m.amount + n.amount); //80.76569546
sys.puts(m+n); //80.76

Ainsi, chaque fois que vous utiliserez un objet Money, celui-ci sera représenté arrondi à deux décimales. La valeur non arrondie est toujours accessible via m.amount.

Vous pouvez créer votre propre algorithme d'arrondi dans Money.prototype.valueOf(), si vous le souhaitez.

5
selfawaresoup

Votre problème provient d'une imprécision dans les calculs en virgule flottante. Si vous utilisez uniquement 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 réfléchir aux mathématiques derrière cela pour le comprendre. Les nombres réels tels que 1/3 ne peuvent pas être représentés en mathématiques 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

Regardez l'implémentation de la solution:http://floating-point-gui.de/languages/javascript/

2
Henry Tseng

use decimaljs ... C'est une très bonne bibliothèque qui résout une partie importante du problème ... 

utilisez-le simplement dans toutes vos opérations. 

https://github.com/MikeMcl/decimal.js/

0
TLEJMI

Si vous souhaitez créer une application vraiment précise, ne faites pas confiance aux bibliothèques JavaScript. à la place, vous pouvez utiliser Web-Assembly.

0
Ehsan sarshar