Calcolo finanziario preciso in JavaScript. Quali sono i trucchi?

Nell’interesse della creazione di codice multipiattaforma, mi piacerebbe sviluppare una semplice applicazione finanziaria in JavaScript. I calcoli richiesti implicano interesse composto e numeri decimali relativamente lunghi. Mi piacerebbe sapere quali errori evitare quando si utilizza JavaScript per fare questo tipo di matematica, se è ansible a tutti!

Probabilmente dovresti ridimensionare i tuoi valori decimali di 100 e rappresentare tutti i valori monetari in centesimi interi. Questo per evitare problemi con la logica in virgola mobile e l’aritmetica . Non esiste alcun tipo di dati decimale in JavaScript: l’unico tipo di dati numerici è a virgola mobile. Pertanto, in genere si consiglia di gestire denaro come 2550 centesimi anziché 25.50 dollari.

Considera che in JavaScript:

 var result = 1.0 + 2.0; // (result === 3.0) returns true 

Ma:

 var result = 0.1 + 0.2; // (result === 0.3) returns false 

L’espressione 0.1 + 0.2 === 0.3 restituisce false , ma per fortuna l’aritmetica dei numeri interi in virgola mobile è esatta, quindi gli errori di rappresentazione decimale possono essere evitati con il ridimensionamento 1 .

Si noti che mentre l’insieme dei numeri reali è infinito, solo un numero finito di essi (18.437.736.874.454.810.627 per l’esattezza) può essere rappresentato esattamente dal formato a virgola mobile JavaScript. Pertanto la rappresentazione degli altri numeri sarà un’approssimazione del numero effettivo 2 .


1 Douglas Crockford: JavaScript: Le parti buone : Appendice A – Parti terribili (pagina 105) .
2 David Flanagan: JavaScript: La guida definitiva, quarta edizione : 3.1.3 Letterali a virgola mobile (pagina 31) .

Scalare ogni valore per 100 è la soluzione. Farlo a mano è probabilmente inutile, dal momento che puoi trovare librerie che lo fanno per te. Raccomando Moneysafe, che offre un’API funzionale e adatta alle applicazioni ES6:

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

https://github.com/ericelliott/moneysafe

Funziona sia in Node.js che nel browser.

Non esiste un calcolo finanziario “preciso” a causa delle sole due cifre decimali, ma questo è un problema più generale.

In JavaScript, puoi ridimensionare ogni valore di 100 e utilizzare Math.round() ogni volta che si verifica una frazione.

È ansible utilizzare un object per memorizzare i numeri e includere l’arrotondamento nel suo metodo valueOf() prototipi. Come questo:

 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 

In questo modo, ogni volta che utilizzi un object Money, verrà rappresentato come arrotondato a due decimali. Il valore non arrotondato è ancora accessibile tramite m.amount .

Puoi build il tuo algoritmo di arrotondamento in Money.prototype.valueOf() , se vuoi.

Il tuo problema deriva dall’inesattezza nei calcoli in virgola mobile. Se stai usando l’arrotondamento per risolvere questo problema avrai un errore maggiore quando ti stai moltiplicando e dividendo.

La soluzione è di seguito, segue una spiegazione:

Dovrai pensare alla matematica dietro a questo per capirlo. I numeri reali come 1/3 non possono essere rappresentati in matematica con valori decimali poiché sono infiniti (ad esempio – .333333333333333 …). Alcuni numeri decimali non possono essere rappresentati correttamente in binario. Ad esempio, 0.1 non può essere rappresentato in binario correttamente con un numero limitato di cifre.

Per una descrizione più dettagliata guarda qui: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Dai un’occhiata all’implementazione della soluzione: http://floating-point-gui.de/languages/javascript/