丸め誤差のため、通貨値を格納するのに float が適切でないことはわかっています。C++ でお金を表す標準的な方法はありますか?
ブーストライブラリを調べましたが、何も見つかりませんでした。Javaでは、BigIntegerがその方法のようですが、C++で同等のものを見つけることができませんでした. 独自のマネー クラスを作成することもできますが、テスト済みのものがある場合は作成したくありません。
税金と利子を掛け合わせるとすぐに誤差が蓄積されるため、セントとして保管しないでください。少なくとも、有効数字 2 桁を余分に保持してください。12.45 ドルは 124,500 として保存されます。符号付き 32 ビット整数のままにしておくと、200,000 ドル (正または負) の作業が必要になります。より大きな数値またはより高い精度が必要な場合は、符号付き 64 ビット整数を使用すると、長時間必要なすべてのスペースが得られる可能性があります。
この値をクラスにラップして、これらの値を作成し、それらに対して演算を実行し、表示用に書式設定するための 1 つの場所を提供すると、役立つ場合があります。これにより、保管されている通貨 (USD、CAD、EURO など) を持ち運ぶ中心的な場所にもなります。
実際の金融システムでこれを扱ってきたので、少なくとも小数点以下 6 桁の精度の数値を使用したいと思うでしょう (米ドルを想定)。うまくいけば、あなたは通貨の価値について話しているので、ここで途方に暮れることはありません. C++ に 10 進型を追加する提案はありますが、実際に出回っているものはまだありません。
ここで使用する最適なネイティブ C++ 型は long double です。
単純に int を使用する他のアプローチの問題は、セント以上のものを保存する必要があることです。多くの場合、金融取引は整数以外の値で乗算され、100.25 ドルを 10025 * 0.000123523 (例: APR) に変換すると問題が発生するため、問題が発生します。最終的には浮動小数点の土地に行き着き、変換には多くの費用がかかります。
現在、ほとんどの単純な状況では問題は発生しません。正確な例を挙げます:
数千の通貨値が与えられた場合、それぞれにパーセンテージを掛けてから合計すると、十分な小数点以下の桁数を保持しないと、合計にそのパーセンテージを掛けた場合とは異なる数値になります。これは状況によってはうまくいくかもしれませんが、多くの場合、すぐに数ペニーになります。私の一般的な経験では、小数点以下 6 桁までの精度を維持していることを確認してください (残りの精度が整数部分で利用可能であることを確認してください)。
また、精度の低い方法で計算を行う場合は、どのタイプで保存しても問題ないことを理解してください。計算が単精度ランドで行われている場合、倍精度で格納しているかどうかは問題ではありません。あなたの精度は、最も正確でない計算に対して正しいでしょう。
とはいえ、単純な加算または減算以外の計算を行わずに数値を格納する場合は問題ありませんが、それよりも複雑なものが現れるとすぐに問題が発生します。
比較的最近のIntelr Decimal Floating-Point Math Library を調べてください。これは特に金融アプリケーション向けであり、2 進浮動小数点演算 (IEEE 754r) の新しい標準の一部を実装しています。
最大の問題は丸め自体です。
42,50 € の 19% = 8,075 €。ドイツの四捨五入規則により、これは 8.08 € です。問題は、(少なくとも私のマシンでは) 8,075 を double として表現できないことです。デバッガーの変数をこの値に変更しても、8,0749999 になります....
そして、これは私の丸め関数 (および私が考えることができるその他の浮動小数点ロジック) が失敗する場所です。有効桁数が 4 であるため、値は切り捨てられます。そして、それは明らかに間違っており、可能な限り浮動小数点値を使用しない限り、何もできません。
42,50 € を Integer 42500000 として表すとうまくいきます。
42500000 * 19 / 100 = 8075000. これで、8080000 を超える丸めルールを適用できます。これは、表示上の理由から通貨値に簡単に変換できます。8,08 €。
しかし、私はいつもそれをクラスにまとめます。
10 進数データ型を試すことができます。
https://github.com/vpiotr/decimal_for_cpp
通貨指向の値 (マネー バランス、通貨レート、金利)、ユーザー定義の精度を格納するように設計されています。19桁まで。
これは、C++ のヘッダーのみのソリューションです。
ドルではなくセント数の変数を保持することをお勧めします。これにより、丸め誤差が解消されます。標準のドル/セント形式で表示することは、ビューの問題です。
あなたのデータの範囲を知ってください。
float は 6 ~ 7 桁の精度にのみ適しているため、丸めなしの最大値は約 +-9999.99 です。ほとんどの金融アプリケーションでは役に立ちません。
double は 13 桁に適しています。つまり、+-99,999,999,999.99 ですが、大きな数を使用する場合は注意が必要です。2 つの類似した結果を差し引くと、精度の多くが失われることに注意してください (潜在的な問題については、数値解析に関する本を参照してください)。
32 ビット整数は +-20 億まで有効です (ペニーにスケーリングすると、小数点以下 2 桁が削除されます)
64 ビット整数はすべてのお金を処理しますが、アプリで浮動小数点数/倍精度数の可能性があるさまざまなレートを変換したり乗算したりするときは注意してください。
重要なのは、問題のドメインを理解することです。正確性についてどのような法的要件がありますか? 値をどのように表示しますか? どのくらいの頻度で変換が行われますか? 国際化は必要ですか?決定を下す前に、これらの質問に答えられることを確認してください。
どのタイプを決定するにしても、別のときに変更できるように、「typedef」にまとめることをお勧めします。
丸めに関するビジネス要件によって異なります。最も安全な方法は、整数を必要な精度で格納し、いつ/どのように丸めを適用するかを知ることです。
ドルとセントの金額を 2 つの別々の整数として保存します。
整数、常に - セントとして保存します (または、プログラミングしている最低通貨が何であれ)。それを浮動小数点で。実際の通貨の計算は進行中に丸められるため、土壇場での丸めは答えではありません。
操作の順序を変更しても問題を回避することはできません。適切なバイナリ表現がないパーセンテージがある場合、これは失敗します。会計士は、あなたが 1 ペニーもずれていると気が狂います。
10 進数ベースの通貨が使用されている場合は、long int を使用して通貨を最小単位 (たとえば、アメリカの通貨はセント) で格納することをお勧めします。
非常に重要: 実際に含まれているものに従って、すべての通貨値に名前を付けてください。(例: account_balance_cents) これにより、多くの問題を回避できます。
(これが現れる別の例はパーセンテージです。100 倍されていない比率が実際に含まれている場合は、値に「XXX_percent」という名前を付けないでください。)
1 つのオプションは、$10.01 を 1001 として保存し、すべての計算をペニーで行い、値を表示するときに 100D で割ることです。
または、フロートを使用して、可能な限り最後の瞬間にのみラウンドします。
多くの場合、操作の順序を変更することで問題を軽減できます。
10% 割引の値 * .10 の代わりに、(値 * 10)/100 を使用すると、非常に役立ちます。(.1 は繰り返しバイナリであることを思い出してください)
当金融機関は「二重」を使用しています。私たちは「債券」ショップであるため、とにかく double を使用する厄介で複雑なアルゴリズムがたくさんあります。秘訣は、エンドユーザーへのプレゼンテーションが double の精度を超えないようにすることです。たとえば、合計数兆ドルの取引のリストがある場合、丸めの問題のためにゴミを印刷しないようにする必要があります。
先に進んで、自分のお金 ( http://junit.sourceforge.net/doc/testinfected/testing.htm ) または currency () クラス (必要なものに応じて) を記述します。そしてそれをテストします。