web-dev-qa-db-ja.com

C ++で通貨値を保存する最良の方法

丸め誤差のため、浮動小数点数は通貨の値を格納するのに適切ではないことを知っています。 C++でお金を表す標準的な方法はありますか?

ブーストライブラリを調べましたが、何も見つかりませんでした。 Javaでは、BigIntegerが道のようですが、C++で同等のものを見つけることができませんでした。私は自分のお金のクラスを書くことができますが、テストされたものがある場合はそうしないことを好みます。

54
Javier Ramos

税金や利子をかなり迅速に掛け合わせるとエラーが蓄積されるため、セントとして保存しないでください。少なくとも、有効数字を2桁余分に保持してください。12.45ドルは124,500として保存されます。符号付き32ビット整数で保持すると、200,000ドル(正または負)の作業ができます。より大きな数値またはより高い精度が必要な場合は、符号付き64ビット整数を使用すると、長い間必要なすべてのスペースを確保できます。

これらの値をクラスでラップして、これらの値を作成し、それらに対して演算を行い、表示用にフォーマットするための1つの場所を提供することは、何らかの助けになるかもしれません。これにより、保管されている通貨(USD、CAD、EUROなど)を持ち運ぶための中心的な場所も提供されます。

31
Eclipse

実際の金融システムでこれを処理したので、少なくとも小数点以下6桁の精度の数値を使用することをお勧めします(USDを想定)。通貨の価値について話しているので、ここから抜け出すことはできません。 10進型をC++に追加する提案はありますが、実際にまだ出回っているものは知りません。

ここで使用するのに最適なネイティブC++型はlong doubleです。

単純にintを使用する他のアプローチの問題は、セント以上のものを保存する必要があることです。多くの場合、金融取引には非整数値が掛けられ、1025ドルが10025 * 0.000123523(例:APR)に換算されると問題が発生するため、問題が発生します。最終的には浮動小数点の土地になり、変換には多大な費用がかかります。

現在、ほとんどの単純な状況では問題は発生しません。正確な例を示します。

数千の通貨値がある場合、それぞれにパーセンテージを掛けてから合計すると、十分な小数点以下の桁数を保持しない場合、合計にそのパーセンテージを掛けた場合とは異なる数値になります。現在、これはいくつかの状況で機能するかもしれませんが、多くの場合、非常に迅速に数ペニーオフになります。私の一般的な経験では、小数点以下6桁までの精度を維持することを確認します(残りの精度が整数部分で使用できることを確認します)。

また、数学をあまり正確に行わない場合は、保存するタイプは関係ないことも理解してください。計算が単精度の土地で行われている場合、それを倍精度で保存するかどうかは関係ありません。あなたの精度は最も精度の低い計算に対して正しいでしょう。


さて、単純な加算または減算以外の数学を行わずに数値を保存する場合は問題ありませんが、それより複雑なものが現れるとすぐに問題が発生します。

21
Orion Adrian

比較的最近の Intelr Decimal Floating-Point Math Library に注目してください。特に金融アプリケーション向けであり、 バイナリ浮動小数点演算の新しい標準(IEEE 754r) の一部を実装しています。

20
jblocksom

最大の問題は、それ自体を丸めることです!

42,50€の19%= 8,075€。丸めに関するドイツの規則により、これは8,08ユーロです。問題は、(少なくとも私のマシンでは)8,075をdoubleとして表現できないことです。デバッガーで変数をこの値に変更しても、8,0749999になります。

そして、これは8,07ユーロを生成するため、私の丸め関数(および私が考えることができる浮動小数点ロジック上の他のもの)が失敗する場所です。有効数字は4であるため、値は切り捨てられます。そして、それは明らかに間違っており、可能な限り浮動小数点値の使用を避けない限り、それについて何もできません。

整数42500000として42,50€を表す場合、うまく機能します。

42500000 * 19/100 =8075000。8080000を超える丸めルールを適用できるようになりました。これは、表示上の理由から通貨値に簡単に変換できます。 8,08ユーロ。

しかし、私は常にクラスでそれをラップします。

11
user331471

ドルではなくセント数の変数を保持することをお勧めします。これにより、丸め誤差が除去されます。標準のドル/セント形式で表示することは、ビューの懸念事項です。

8
Rontologist

どのタイプを決定するにしても、別のタイミングで変更できるように「typedef」でラップすることをお勧めします。

5
Jordan Parmer

10進データ型を試すことができます。

https://github.com/vpiotr/decimal_for_cpp

お金志向の値(お金のバランス、通貨レート、金利)、ユーザー定義の精度を格納するように設計されています。最大19桁。

C++用のヘッダーのみのソリューションです。

5
Piotr

あなたのデータの範囲を知る。

フロートは6〜7桁の精度でのみ有効であるため、丸めなしで最大約+ -9999.99を意味します。ほとんどの金融アプリケーションには役に立ちません。

Doubleは13桁に適しています。したがって、+-99,999,999,999.99、大きな数値を使用する場合は注意が必要です。 2つの類似した結果を減算すると、精度が大幅に低下します(潜在的な問題については、数値解析に関する本を参照してください)。

32ビット整数は+ -20億まで有効です(ペニーへのスケーリングは小数点以下2桁を落とします)

64ビット整数はお金を処理しますが、繰り返しますが、浮動小数点数や倍精度数である可能性のあるアプリのさまざまなレートで変換および乗算する場合は注意してください。

重要なのは、問題のあるドメインを理解することです。正確さについてどのような法的要件がありますか?値をどのように表示しますか?変換はどのくらいの頻度で行われますか?国際化が必要ですか?決定を下す前に、これらの質問に答えられることを確認してください。

5
Dan Hewett

あなたはあなたがブーストライブラリを見たが、そこについて何も見つけられなかったと言います。しかし、そこには multiprecision/cpp_dec_float があります:

このタイプの基数は10です。その結果、base-2タイプとは微妙に異なる動作をすることができます。

したがって、既にBoostを使用している場合、これは基本の10の数値と50または100桁の精度(多く)であるため、通貨の値と操作に適しているはずです。

見る:

#include <iostream>
#include <iomanip>
#include <boost/multiprecision/cpp_dec_float.hpp>

int main()
{
    float bogus = 1.0 / 3.0;
    boost::multiprecision::cpp_dec_float_50 correct = 1.0 / 3.0;

    std::cout << std::setprecision(16) << std::fixed 
              << "float: " << bogus << std::endl
              << "cpp_dec_float: " << correct << std::endl;

    return 0;
}

出力:

フロート:0.3333333432674408

cpp_dec_float:0.3333333333333333

* float(基数2)が悪く、小数(基数10)が良いと言っているわけではありません。彼らはただ異なる振る舞いをします...

**これは古い投稿であり、2013年にboost :: multiprecisionが導入されたので、ここで説明したいと思います。

5
Fernando

丸めに関するビジネス要件によって異なります。最も安全な方法は、必要な精度で整数を保存し、いつ/どのように丸めを適用するかを知ることです。

4
Douglas Mayle

整数、常に-セント(またはプログラミング対象の最低通貨が何であっても)として保存します。問題は、いつ浮動小数点を使用しても、計算が異なると状況が変わることです。浮動小数点で。実際の通貨の計算は進むにつれて丸められるため、最後の最後の丸めは答えではありません。

操作の順序を変更しても問題を回避することはできません。適切なバイナリ表現が得られない割合がある場合、これは失敗します。会計士は、あなたが1ペニーずらしている場合、気が狂います。

2
Loren Pechtel

小数ベースの通貨が使用されている場合は、long intを使用して通貨を最小単位で保存することをお勧めします(たとえば、アメリカのお金はセントです)。

非常に重要:すべての通貨の値には、実際に含まれているものに応じて名前を付けてください。 (例:account_balance_cents)これにより、将来の多くの問題を回避できます。

(これが発生する別の例はパーセントです。100倍ではない比率が実際に含まれている場合、値に「XXX_percent」という名前を付けないでください。)

1

解決策は単純であり、必要な精度に合わせてシフトされた整数として保存します。ただし、読み込み時にdouble floatに変換すると、計算の丸め誤差が少なくなります。次に、データベースに格納するときに必要な整数精度に乗算しますが、整数として切り捨てる前に、切り捨てエラーを補正するために+/- 1/10を追加するか、丸めるために+/- 51/100を追加します。簡単です。

1
Laurie

先に進み、自分のお金( http://junit.sourceforge.net/doc/testinfected/testing.htm )または通貨()クラス(必要なものに応じて)を書きます。そしてそれをテストします。

0
Ray Tayek

私たちの金融機関は「ダブル」を使用しています。私たちは「債券」ショップなので、とにかくダブルを使用する厄介な複雑なアルゴリズムがたくさんあります。秘Theは、エンドユーザーのプレゼンテーションがdoubleの精度を超えないようにすることです。たとえば、合計で数兆ドルの取引のリストがある場合、丸めの問題のためにゴミを印刷しないようにする必要があります。

0
Arkadiy

32ビットにはsigned longを、64ビットにはsigned long longを使用します。これにより、基礎となる数量自体の最大ストレージ容量が得られます。次に、2つのカスタムマニピュレーターを開発します。 1つは為替レートに基づいてその数量を変換し、もう1つは選択した通貨にその数量をフォーマットします。さまざまな金融業務/およびルール用のマニピュレーターをさらに開発できます。

0
user2074102

[〜#〜] gmp [〜#〜] ライブラリには、お金を扱うのに必要な任意のサイズの整数計算に使用できる「bignum」実装があります。 mpz_class(警告:これは恐ろしく不完全ですが、算術演算子の全範囲が提供されています)のドキュメントを参照してください。

0
Greg Rogers

1つのオプションは、$ 10.01を1001として保存し、値を表示するときに100Dで除算して、すべての計算を小額で行うことです。

または、フロートを使用し、可能な限り最後の時点でのみラウンドします。

多くの場合、操作の順序を変更することで問題を軽減できます。

10%割引の値* .10の代わりに、(値* 10)/ 100を使用します。これは非常に役立ちます。 (.1は繰り返しバイナリであることに注意してください)

0
Chris Cudmore

ドルとセントを2つの別々の整数として保存します。

0
kmiklas