web-dev-qa-db-ja.com

お金のためにフロートまたはダブルを使用してきたシステムの損傷をどのように評価しますか?

私は四半期ごとに数百万ドルのトランザクションを担当するコードベースに割り当てられ、10年以上使用されています。ソリューションをふるいにかけると、お金を表すためにどこでもdoublesが使用され、これらの変数に対して算術演算が行われます。まれにdecimalタイプが使用されます。

通貨に不適切なタイプを使用することによる丸め誤差によって生じる可能性のある損傷の程度を理解するための適切なアプローチは何ですか?

3
8protons

お金のために単精度浮動小数点数を使用することは致命的です。 Floatの精度は24ビットしかないため、ドルを使用している場合、2 ^ 18ドルを超えるものは1セントよりも解像度が低くなります。つまり、263,000ドル以上には大きな問題があります。

倍精度を使用しても問題ははるかに少なくなります。倍精度で1兆ドルの金額がある場合、その数値の最下位ビットの値は2 ^ -14ドルで、これは1セントの約1/164です。その範囲の数値の丸め誤差はその半分です。 10億ドルの場合、エラーは1,000分の1になります。

丸め誤差を調べる必要がある場合、それらは無視できます。特定のルールに従って実行する必要がある計算があり、最終的に正確な量で計算する必要があり、(費用のかかる)法的結果がある場合、問題が発生する可能性があります。

結果が四捨五入されている場合(セントなど)、その四捨五入がどのように行われたかを注意深く調べます。系統的なエラーを引き起こす可能性のあるバイアスがかかる可能性があります。

8
gnasher729

あなたは、金銭のフロートの使用について心配しているのは正しいです。丸められたスケールの小数(または同等のセントの整数)として適切に表されるものを計算するためだけに使用されない限り、それらはお金を表すために使用されるべきではありません。

ただし、発生する可能性のある損傷は、これらの値の正確な使用に大きく依存するため、これらの数値がトランザクションやレポートで使用されている場所、および合計の不一致が発生している可能性がある場所を見つける必要があります。

丸め誤差が体系的に悪意のある開発者のプライベート銀行口座にサイフォンで吸い込まれていない限り(これは過去にどこかで正しく覚えていればどこかで起こりました)、会社またはその顧客への直接的な金銭的損害はおそらく妥当な範囲内ですが、あなたはできません分析を行う前に確認してください。問題を修正するために必要な労力は、損傷よりも数桁大きい場合がありますが、それでも避けられない場合があります。

3

損害を会社が失った金額と見なすと、ランダムで非常にゼロに近くなります。デルタに近づく操作が多いほど、ゼロに近くなります。まれなケース(ダブルの場合)で、セントを失う場合があります-ゲイン。偏った丸め方法が使用されていない限り、小計はゼロに近づきます。アプリがdoubleを使用する場合、エラーの可能性は非常に低くなります。私はその間違いをしました。データベースまでずっとdoubleを使用する財務会計アプリケーションを構築しました。私の間違いを理解したとき、それは遅すぎました。本番環境でデータベースをdouble型からdecimal型に変更すると、sqliteの問題点は言うまでもなく、無効な型自体よりも大きな損害が発生する可能性があります。約5年前に、最も使用頻度の高いいくつかのエンティティーを10進数で再計算するテストをdb整合性ヘルスルーチンに追加しました。現在のところ-1セントの差異は検出されていません。数百人のユーザー、小さな会社。

1
Hobbyist

浮動小数点数は、正確ではあるが任意の数値を格納するのには適していません。すべての浮動小数点数は、数値の近似値です。確かに、一部の数値は浮動小数点値として正確に表すことができますが、多くの数値はそうでなく、大きく変動する可能性があるエラーが発生するため、常に近似値として扱うことをお勧めします。例えば。 0.2のような単純な値でも、double値として正確に表すことはできません。 C#で0.2 + 0.1を計算すると(両方がdouble値であると想定)、0.30000000000000004が返されますが、0.3は正しかったでしょう。

お金は実際には整数として簡単に表すことができ、セント値として保存するだけです。 64ビット整数値は、このタスクには適していません。計算中に、最初にそれをより高い精度の値(小数など)に変換し、演算を実行し、最後に適切な丸めを使用して最後にセントに戻すことができますが、多くのタスクでは必要ありません(加算、減算、および乗算は整数値で問題ありません)。

エラーを判別する際の問題は、エラーが合計されることです。利息を計算する場合、誤差は小さすぎて有意ではありませんが、莫大な金額の複利を計算し、その結果を他の計算で使用すると、誤差が大きくなる可能性があります。したがって、1つの操作のエラーが何であるかを確認しただけでは、この操作は一連の操作の1つの操作として使用され、最終的に1つの結果を計算するため、役に立たない場合があります。

また、エラーは長年にわたって合計することができます。口座残高を2倍として保存し、表示する期間の後は常に値を2桁に丸めますが、内部的には完全な歳差を維持している場合、何年も正しい結果が得られ、30年後に突然1つずつずれてしまいます。セントは、アカウントの残高だけでなく、年間のトランザクション数にも依存します。確かに、ほとんどの人は「1セントはどこにも意味がない」と考えます。ええと、あなたが金融機関で1億のアカウントがあり、それぞれが1セントだけずれている場合、合計残高は100万ドルずれているので、私はそれを重要と呼んでいます。

あなたのコードが正確に何を計算しているか、中間丸めなしでどのくらいの頻度で計算しているのかわかりませんので、ここでより良い答えを提供することはできません。例えば。実際に送金されただけの場合(アカウントAから差し引かれ、アカウントBに追加されます)、doubleを使用しても大きな問題は簡単には起こりません。通常、重要なエラーをすばやく累積するには、乗算、指数/平方根を含むより複雑な演算が必要です。

1
Mecki