web-dev-qa-db-ja.com

10進数と2倍速

私は、小数点を使用するのではなく、倍を使用するという決断と常に戦う金融アプリケーションを作成しています。

私の数学はすべて、小数点以下5桁以下の数値で機能し、最大100,000以下です。これらはすべて、丸め誤差なしにダブルとして表現できると感じていますが、確信が持てませんでした。

私は先に進み、明らかな速度の利点のために小数から倍に切り替えますが、1日の終わりに、ToStringメソッドを使用して価格を取引所に送信し、常に数値を出力することを確認する必要があります。期待する。 (89.99000000001ではなく89.99)

質問:

  1. 速度の利点は、単純なテストが示唆するのと同じくらい大きいですか? (〜100回)
  2. ToStringからの出力が希望どおりであることを保証する方法はありますか?これは私の番号が常に表現可能であるという事実によって保証されますか?

更新:アプリを実行する前に約100億件の価格更新を処理する必要があります。保護上の理由から、現時点では10進数で実装していますが、オンにするだけで最大3時間かかります。 。ダブルスでそれを行う安全な方法はありますか?

48
Superman
  1. 浮動小数点演算は、ハードウェアによって直接サポートされているため、ほとんどの場合、かなり高速になります。これまでのところ、広く使用されているハードウェアは10進演算をサポートしていません(ただし、これは変更されていますが、コメントを参照してください)。
  2. 金融アプリケーションはalways小数を使用する必要があります。金融アプリケーションで浮動小数点を使用することに起因するホラーストーリーの数は無限であり、Google検索でそのような例を数多く見つけることができるはずです。
  3. 10進演算は浮動小数点演算よりも大幅に遅くなることがありますが、10進データの処理にかなりの時間を費やさない限り、プログラムへの影響はごくわずかです。いつものように、違いを心配する前に適切なプロファイリングを行ってください。
79
Robert Gamble

ここには2つの分離可能な問題があります。 1つは、必要なすべてのビットを保持するのに十分な精度がdoubleにあるかどうかであり、もう1つは、数値を正確に表現できる場所です。

正確な表現については、1/10のような正確な小数部には正確な2進数の対応物がないため、注意が必要です。ただし、小数点以下5桁の精度しか必要ないことがわかっている場合は、10- ^ 5を掛けた数値を演算するscaled演算を使用できます。したがって、たとえば、23.7205を正確に表す場合は、2372050として表します。

十分な精度があるかどうかを見てみましょう。倍精度では53ビットの精度が得られます。これは15桁以上の10進数の精度に相当します。したがって、これにより、小数点の後ろに5桁、小数点の前に10桁を使用できます。これは、アプリケーションにとって十分なようです。

私はこのCコードを.hファイルに入れます:

typedef double scaled_int;

#define SCALE_FACTOR 1.0e5  /* number of digits needed after decimal point */

static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }

static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }

void fprint_scaled(FILE *out, scaled_int x) {
  fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}

おそらくいくつかの荒い箇所がありますが、それであなたは始めるのに十分でしょう。

加算のオーバーヘッドはなく、乗算または除算の倍のコスト。

C99にアクセスできる場合は、int64_t 64ビット整数型を使用して、スケーリングされた整数演算を試すこともできます。どちらが速いかは、ハードウェアプラットフォームによって異なります。

23
Norman Ramsey

財務計算には常にDecimalを使用してください。そうしないと、1セントの丸め誤差を永遠に追跡することになります。

20
Craig
  1. はい;ソフトウェアの計算は、実際にはハードウェアの100倍遅くなります。または、少なくとも、それははるかに遅く、100の係数は、1桁の大きさを与えるか、または取る程度で、ほぼ適切です。 80386ごとに80387浮動小数点コプロセッサが搭載されているとは想定できなかった昔の時代に、バイナリ浮動小数点のソフトウェアシミュレーションもあり、それは低速でした。
  2. 番号;純粋な2進浮動小数点がすべての10進数を正確に表すことができると考える場合、ファンタジーの土地に住んでいます。 2進数は、2分の1、4分の1、8分の1などを組み合わせることができますが、正確な10進数0.01の場合、1/5の2つの因数と1/4の1つの因数が必要になります(1/100 =(1/4)*(1/5)*(1/5))そして5分の1は2進数で正確に表現できないため、すべての10進数値を2進数値で正確に表すことはできません(0.01は正確に表すことができない反例ですが、10進数の巨大なクラスの代表であるため、正確に表すことはできません)。

したがって、ToString()を呼び出す前に丸めを処理できるかどうか、または結果が文字列に変換されるときに結果の丸めを処理する他のメカニズムを見つける必要があるかどうかを決定する必要があります。または、10進数演算は引き続き正確であるため、引き続き使用できます。ハードウェアで新しいIEEE 754 10進数演算をサポートするマシンがリリースされると、より高速になります。

必須の相互参照: すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと 。これは、考えられる多くのURLの1つです。

10進数演算と新しいIEEE 754:2008標準に関する情報は、この Speleotrove サイトにあります。

12

Longを使用し、10の累乗を乗算します。完了したら、同じ10の累乗で除算します。

8
John

小数は常に財務計算に使用する必要があります。数字のサイズは重要ではありません。

説明する最も簡単な方法は、いくつかのC#コードを使用することです。

double one = 3.05;
double two = 0.05;

System.Console.WriteLine((one + two) == 3.1);

このコードは、3.1が3.1と等しい場合でもFalseを出力します...

同じことですが、10進数を使用します。

decimal one = 3.05m;
decimal two = 0.05m;

System.Console.WriteLine((one + two) == 3.1m);

これで出力されますTrue

この種の問題を回避したい場合は、小数を使用することをお勧めします。

6
mezoid

この質問 に対する私の回答を紹介します。

長い値を使用し、追跡する必要がある最小量を保存して、それに応じて値を表示します。

1
gbjbaanb