wake-up-neo.com

Präzise Finanzberechnung in JavaScript. Was sind die Gotchas?

Im Interesse der Erstellung von plattformübergreifendem Code möchte ich eine einfache Finanzanwendung in JavaScript entwickeln. Die erforderlichen Berechnungen beinhalten Zinseszinsen und relativ lange Dezimalzahlen. Ich würde gerne wissen, welche Fehler zu vermeiden sind, wenn JavaScript für diese Art von Mathematik verwendet wird - wenn überhaupt möglich! 

106
james_womack

Sie sollten Ihre Dezimalwerte wahrscheinlich um 100 skalieren und alle Geldwerte in ganzen Cent darstellen. Damit vermeiden Sie Probleme mit der Gleitkommalogik und der Arithmetik . Es gibt keinen dezimalen Datentyp in JavaScript - der einzige numerische Datentyp ist Fließkommazahl. Daher wird generell empfohlen, Geld als 2550 Cent anstelle von 25.50 Dollar zu behandeln. 

Beachten Sie das in JavaScript: 

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

Aber:

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

Der Ausdruck 0.1 + 0.2 === 0.3 gibt false zurück. Glücklicherweise ist die Ganzzahlarithmetik in Fließkommazahlen genau, sodass Dezimaldarstellungsfehler durch Skalierung vermieden werden können1.

Während der Satz reeller Zahlen unendlich ist, kann nur eine begrenzte Anzahl von ihnen (18.437.736.874.454.810.627) durch das JavaScript-Gleitkommaformat genau dargestellt werden. Daher ist die Darstellung der anderen Zahlen eine Annäherung an die tatsächliche Zahl2.


1 Douglas Crockford: JavaScript: Die guten Teile: Anhang A - Schreckliche Teile (Seite 105) .
2 David Flanagan: JavaScript: Der endgültige Leitfaden, Vierte Auflage: 3.1.3 Fließkomma-Literale (Seite 31) .

93
Daniel Vassallo

Jeder Wert um 100 zu skalieren ist die Lösung. Es ist wahrscheinlich nutzlos, es von Hand zu tun, da Sie Bibliotheken finden können, die das für Sie tun. Ich empfehle moneysafe, das eine funktionale API bietet, die sich gut für ES6-Anwendungen eignet:

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

https://github.com/ericelliott/moneysafe

Funktioniert sowohl in Node.js als auch im Browser.

11

Es gibt keine "genaue" Finanzkalkulation, da es nur zwei Dezimalstellen gibt, aber das ist ein allgemeineres Problem.

In JavaScript können Sie jeden Wert um 100 skalieren und Math.round() jedes Mal verwenden, wenn ein Bruch auftreten kann.

Sie können ein Objekt verwenden, um die Zahlen zu speichern und die Rundung in die valueOf()-Methode der Prototypen aufzunehmen. So was:

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

Auf diese Weise wird jedes Mal, wenn Sie ein Money-Objekt verwenden, dieses auf zwei Dezimalstellen gerundet dargestellt. Der ungerundete Wert ist weiterhin über m.amount erreichbar.

Sie können Ihren eigenen Rundungsalgorithmus in Money.prototype.valueOf() einbauen, wenn Sie möchten.

5
selfawaresoup

Ihr Problem ergibt sich aus Ungenauigkeiten bei der Berechnung von Fließkommazahlen. Wenn Sie nur das Runden verwenden, um dieses Problem zu lösen, haben Sie größere Fehler, wenn Sie multiplizieren und dividieren. 

Die Lösung ist unten, eine Erklärung folgt:

Sie müssen über Mathematik dahinter nachdenken, um es zu verstehen. Reelle Zahlen wie 1/3 können nicht mit Dezimalzahlen in Mathematik dargestellt werden, da sie endlos sind (z. B. - .333333333333333 ...). Einige Dezimalzahlen können nicht korrekt binär dargestellt werden. Beispielsweise kann 0.1 mit einer begrenzten Anzahl von Ziffern nicht korrekt binär dargestellt werden. 

Für eine detailliertere Beschreibung siehe hier: http://docs.Oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Schauen Sie sich die Lösungsimplementierung an:http://floating-point-gui.de/languages/javascript/

2
Henry Tseng

verwenden Sie decimaljs ... Es ist eine sehr gute Bibliothek, die einen großen Teil des Problems löst ... 

verwenden Sie es einfach für alle Ihre Operationen. 

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

0
TLEJMI

Wenn Sie eine wirklich präzise Anwendung erstellen möchten, sollten Sie JavaScript-Bibliotheken nicht vertrauen. Stattdessen können Sie Web-Assembly verwenden.

0
Ehsan sarshar