web-dev-qa-db-ja.com

JavaScriptでの正確な財務計算。落とし穴は何ですか?

クロスプラットフォームコードを作成するために、JavaScriptで簡単な金融アプリケーションを開発したいと思います。必要な計算には、複利と比較的長い10進数が含まれます。 JavaScriptを使用してこの種の計算を行う場合に回避すべき誤りを知りたいのですが、可能であれば!

110
james_womack

おそらく、10進数値を100でスケーリングし、すべての通貨値をセント単位で表す必要があります。これは、 浮動小数点のロジックと算術の問題 を避けるためです。 JavaScriptには10進データ型はありません-数値データ型は浮動小数点のみです。したがって、一般的に、お金を2550ドルではなく25.50セントとして扱うことをお勧めします。

JavaScriptで次のことを考慮してください。

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

しかし:

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

0.1 + 0.2 === 0.3falseを返しますが、幸いなことに浮動小数点の整数演算は正確であるため、スケーリングにより10進表現エラーを回避できます。1

実数のセットは無限ですが、それらの有限数(正確には18,437,736,874,454,810,627)のみがJavaScript浮動小数点形式で正確に表現できることに注意してください。したがって、他の数値の表現は、実際の数値の近似値になります2


1 ダグラス・クロックフォード: JavaScript:The Good Parts:付録A-ひどいパーツ(105ページ)
2 David Flanagan: JavaScript:The Definitive Guide、Fourth Edition:3.1.3浮動小数点リテラル(31ページ) .

100
Daniel Vassallo

すべての値を100でスケーリングすることが解決策です。手作業で行うのはおそらく役に立たないでしょう。 ES6アプリケーションに適した機能的なAPIを提供するmoneysafeをお勧めします。

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

https://github.com/ericelliott/moneysafe

Node.jsとブラウザーの両方で機能します。

13

小数桁が2桁しかないため、「正確な」財務計算などはありませんが、それはより一般的な問題です。

JavaScriptでは、すべての値を100でスケーリングし、小数部が発生するたびにMath.round()を使用できます。

オブジェクトを使用して数値を保存し、プロトタイプのvalueOf()メソッドに丸めを含めることができます。このような:

_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
_

そうすれば、Moneyオブジェクトを使用するたびに、小数点以下2桁に丸められて表されます。丸められていない値には、_m.amount_を介して引き続きアクセスできます。

必要に応じて、独自の丸めアルゴリズムをMoney.prototype.valueOf()に組み込むことができます。

6
selfawaresoup

問題は、浮動小数点計算の不正確さに起因します。これを解決するために丸めだけを使用している場合、乗算と除算を行うとエラーが大きくなります。

解決策は以下のとおりです。説明は次のとおりです。

これを理解するには、この背後にある数学について考える必要があります。 1/3のような実数は、無限であるため(たとえば-.333333333333333 ...)、10進数値で数学的に表すことはできません。 10進数の一部の数値は、バイナリで正しく表現できません。たとえば、0.1は、限られた桁数ではバイナリで正しく表現できません。

詳細については、こちらをご覧ください: http://docs.Oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

ソリューションの実装を見てください:http://floating-point-gui.de/languages/javascript/

2
Henry Tseng

decimaljsを使用します...それは問題の厳しい部分を解決する非常に良いライブラリです...

すべての操作で使用するだけです。

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

1
TLEJMI

非常に正確なアプリケーションを構築する場合は、JavaScriptライブラリを信頼しないでください。代わりに、Web-Assemblyを使用できます。

0
Ehsan sarshar