ペースの速い金融の世界では、正確さがすべてです。四捨五入や精度の誤差が原因で 100 万ドルの損失が発生することは避けたほうが良いでしょう。
この記事の出発点は、お金はかごの中のリンゴを数えるのに使用できる平均的な基本的な数字ではないという認識です。 10ユーロと10ドルを掛けてみると何が得られますか?大変ですね…古いジャケットのポケットから奇跡の 1.546 ドルを見つけたことがありますか?はい、わかっていますが、これも実際には不可能です。これらの愚かな例は、お金には独自の特別なルールがあり、単純な数字だけでモデル化することはできないという事実を説明するためにここに挙げたものです。安心してください、私が最初にそのことに気づいたわけではありません(おそらくあなたは私よりずっと前に気づいていたでしょう)。 2002 年、プログラマーの Martin Fowler は、「エンタープライズ アプリケーション アーキテクチャのパターン」の中で、特定の属性とオペランド ルールを使用してお金を表現する方法を提案しました。彼にとって、お金のデータ型に必要な最小限の実行可能な属性は次の 2 つでした:
amount currency
この本当に基本的な表現は、シンプルだが堅牢な通貨モデルを構築するための出発点になります。
金額は間違いなく特定の数値です。精度は固定されています (繰り返しになりますが、ポケットに 4.376 ドルを入れることはできません)。この制約を尊重できるような表現方法を選択する必要があります。
ネタバレ注意。浮動小数点数表現の暗い世界で数セント (ドルではないとしても) が消えていくのを見たくないのであれば、これは決して良い考えではありません。
JavaScript でのコーディングの経験があれば、最も単純な計算でも、最初は予期しない精度の誤差が生じる可能性があることをご存知でしょう。この現象を強調する最も明白でよく知られた例は次のとおりです:
0.1 0.2 !== 0.3 // true 0.1 0.2 // 0.30000000000000004
この例で十分に納得できない場合は、JavaScript のネイティブ数値型を使用する場合に発生する可能性のあるすべての問題のある計算結果についてもう少し詳しく説明したこの記事を参照することをお勧めします…
結果のこのわずかなデルタは、ユーザーにとっては無害に見えるかもしれません (大きさは約 10^-16) が、重要な金融アプリケーションでは、このようなエラーが急速に連鎖する可能性があります。各取引に同様の計算が含まれる、数千のアカウント間で資金を移動することを検討してください。わずかな誤差が積み重なり、気が付けば財務諸表が数千ドルもずれてしまいます。正直に言うと、お金のことになると、法的にも、顧客との信頼関係を築くためにも、間違いは許されないということには誰もが同意します。
プロジェクトの 1 つで問題に遭遇したとき、私が最初に自問したのは、なぜ?問題の原因は JavaScript ではなく、これらの不正確さは他の最新のプログラミング言語 (Java、C、Python など) にも影響を与えることがわかりました。
// In C #includeint main() { double a = 0.1; double b = 0.2; double sum = a b; if (sum == 0.3) { printf("Equal\n"); } else { printf("Not Equal\n"); // This block is executed } return 0; } // > Not equal
// In Java public class doublePrecision { public static void main(String[] args) { double total = 0; total = 5.6; total = 5.8; System.out.println(total); } } // > 11.399999999999
実際、根本的な原因は、浮動小数点数を表すためにこれらの言語で使用されている規格、つまり、IEEE 754 標準で指定されている倍精度(または単精度)浮動小数点形式にあります。
JavaScript では、ネイティブ型の数値は倍精度浮動小数点数に対応します。これは、数値が 64 ビットでエンコードされ、3 つの部分に分割されることを意味します。
次に、次の式を使用してビット表現を 10 進数値に変換する必要があります:
倍精度浮動小数点数表現の例、1/3 の近似:
0 01111111101 0101010101010101010101010101010101010101010101010101 = (-1)^0 x (1 2^-2 2^-4 2^-6 ... 2^-50 2^-52) x 2^(1021-1023) = 0.333333333333333314829616256247390992939472198486328125 ~ 1/3
この形式を使用すると、広範囲の値を表すことができますが、考えられるすべての数値を絶対的な精度で表すことはできません (0 と 1 の間だけで、無限の数値を見つけることができます…)。多くの数値はバイナリ形式で正確に表現できません。最初の例を繰り返すと、それが 0.1 と 0.2 の問題です。 2 点浮動小数点表現ではこれらの値の近似値が得られるため、これら 2 つの不正確な表現を追加すると、結果も正確ではなくなります。
ネイティブ JavaScript の数値型で金額を処理するのは悪い考えであると完全に確信したので (少なくとも、それについて疑問を持ち始めてほしいと思います)、10 億ドルの問題は どのように進めるべきかです ?解決策としては、JavaScript で使用できる強力な固定精度演算パッケージを利用することが考えられます。たとえば、Decimal.js (人気のある ORM Prisma で Decimal データ型を表すために使用されます) や Big.js.
などです。これらのパッケージは、上で説明した精度誤差を排除して計算を実行できる特別なデータ型を提供します。
// Example using Decimal.js const Decimal = require('decimal.js'); const a = new Decimal('0.1'); const b = new Decimal('0.2'); const result = a.plus(b); console.log(result.toString()); // Output: '0.3'
このアプローチには別の利点があり、表現できる最大値が大幅に拡張され、たとえば暗号通貨を扱う場合に非常に便利になります。
たとえそれが本当に堅牢だったとしても、それは私の Web アプリケーションに実装するために選択するものではありません。 ストライプ戦略を適用して整数値のみを扱う方が簡単かつ明確だと思います。
私たち Theodo Fintech は、実用主義を大切にしています!私たちは、業界で最も成功している企業からインスピレーションを受けることが大好きです。決済サービスを専門とする数十億ドル規模の有名企業である Stripe は、浮動小数点数を使用せずに整数を使用して金額を処理することを選択しました。そのために、通貨の最小単位を使用して金額を表します。
// 10 USD are represented by { "amount": 1000, "currency": "USD" }
これはすでにご存じの方も多いと思いますが、すべての通貨には同じ大きさの最小単位があるわけではありません。それらのほとんどは「小数点 2 桁」の通貨 (EUR、USD、GBP) であり、最小単位が通貨の 100 分の 1 であることを意味します。ただし、一部の通貨は「10 進数 3 桁」の通貨 (KWD) または「10 進数 0 桁」の通貨 (JPY) です。 (詳細については、ISO4217 標準に従ってください)。これらの格差に対処するには、最小単位で表される金額を対応する通貨に変換するための乗算係数を通貨データ表現に統合する必要があります。
もうお分かりかと思いますが、ネイティブ数値、サードパーティ製の任意精度パッケージ、または整数を使用できます。計算により浮動小数点の結果が得られる可能性があり、金額に四捨五入する必要があります。有限の精度。簡単な例はそれほど多くはありませんが、整数値を処理し、8.5413% という非常に正確な金利で 16,000 ドルのローンを契約したとします (ひどい…)。その後、16,000 ドルと追加金額
を返金する必要があります。
1600000 * 0.085413 // amount in cents //Output in cents: 136660.8
重要なのは、計算後の金額の四捨五入処理を適切に処理することです。ほとんどの場合、最終的には 3 種類の丸めから選択する必要があります:
四捨五入に関しては、「魔法のソース」 は実際にはありません。状況に応じて調停する必要があります。新しい通貨や新しい丸めの使用例(両替、お金の分割、クレジットの金利など)を扱うときは、常に法律を確認することをお勧めします。さらなるトラブルを避けるために、すぐに規制に従ったほうがよいでしょう。たとえば、換算レートに関しては、ほとんどの通貨で必要な精度と丸めのルールが定められています (EUR の換算レートのルールはこちらで確認できます)。
この記事は、JavaScript で金額を処理するための既存の可能性をすべて網羅しているわけではありません。また、完全/完璧な金額データ モデルを提供することを目的としたものでもありません。私は、一貫性と回復力が証明され、フィンテック業界の大手企業によって選ばれた表現を実装するための十分なヒントとガイドラインを提供しようと努めました。将来のプロジェクトで、ポケットに 1 セントも忘れずに金額計算を実行できるようになることを願っています (そうでない場合は、古いジャケットを確認することを忘れないでください)!
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3