web-dev-qa-db-ja.com

ダブル対BigDecimal?

私はいくつかの浮動小数点変数を計算しなければなりません、そして、私の同僚はそれがより正確になるのでBigDecimalの代わりにdoubleを使うことを私に提案します。しかし、私はそれが何であるか、そしてどのようにBigDecimalを最大限に活用するかを知りたいですか?

253
Truong Ha

BigDecimalは数値を表現する正確な方法です。 Doubleはある程度の精度を持ちます。さまざまな大きさの倍数(d1=1000.0d2=0.001など)を扱うと、大きさの差が大きいため、合計すると0.001がすべてドロップされる可能性があります。 BigDecimalではこれは起こりません。

BigDecimalのデメリットは、処理速度が遅く、アルゴリズムをそのようにプログラムするのが少し難しいことです(+-*および/がオーバーロードされていないため)。

お金を扱っている場合、または精度が必須の場合は、BigDecimalを使用してください。そうでなければDoublesは十分に良い傾向があります。

BigDecimaljavadoc を読むことをお勧めします。これは、ここで行うよりも説明がうまくいくからです。

376
extraneon

私の英語は苦手なので、ここで簡単な例を書きます。

    double a = 0.02;
    double b = 0.03;
    double c = b - a;
    System.out.println(c);

    BigDecimal _a = new BigDecimal("0.02");
    BigDecimal _b = new BigDecimal("0.03");
    BigDecimal _c = _b.subtract(_a);
    System.out.println(_c);

プログラム出力:

0.009999999999999998
0.01

誰かがまだ二重を使いたいですか? ;)

139
Basil

Doubleとの主な違いは2つあります。

  • BigIntegerと同様に、任意の精度とサイズを含めることができます。
  • 基数2の代わりに基数10のBigDecimalはn * 10 ^ scaleです。ここで、nは任意の大きな符号付き整数で、scaleは小数点を左または右に移動する桁数と見なすことができます。

BigDecimalを通貨計算に使用する必要があるのは、それが任意の数を表すことができるのではなく、10進数で表現できるすべての数を表すことができるためです。誰かへ)。

41
Meros

1 / 7のような小数値を10進数で書き留めれば、

1/7 = 0.142857142857142857142857142857142857142857...

無限のシーケンスの142857を使って。書き込める桁数は有限なので、必然的に丸め(または切り捨て)エラーが発生します。

1/101/100のような小数部を含む2進数として表現された数も、小数点の後に無限の桁数を持ちます。

1/10 = binary 0.0001100110011001100110011001100110...

Doublesは値を2進数として格納するため、算術演算を行わなくても、10進数を2進数に変換するだけでエラーが発生する可能性があります。

一方、10進数(BigDecimalなど)は、各10進数をそのまま格納します。つまり、10進数型は、一般的な意味では2進浮動小数点数または固定小数点数より正確ではありません(例えば、精度を失うことなく1/7を格納することはできません)。お金の計算でよくあることですが。

JavaのBigDecimalには、利用可能なメモリによってのみ制限される、小数点の両側に任意の(ただし有限の)桁数を持つことができるという追加の利点があります。

BigDecimalは、Oracleの任意精度数値ライブラリです。 BigDecimalはJava言語の一部であり、金融​​から科学までさまざまなアプリケーションに適しています(これは一種のamです)。

特定の計算に倍精度を使用しても問題ありません。しかし、あなたがMath.Pi * Math.Pi/6、つまり2つの実引数(現在私が取り組んでいるプロジェクト)に対するRiemann Zeta関数の値を計算したいとします。浮動小数点除算は、丸め誤差の問題を抱えています。

一方、BigDecimalには、任意の精度で式を計算するための多くのオプションがあります。下記のOracleのドキュメントで説明されているように、BigDecimal Java Worldの+、*、および/の「代わりにする」加算、乗算、除算の各メソッド:

http://docs.Oracle.com/javase/7/docs/api/Java/math/BigDecimal.html

CompareToメソッドは、whileループとforループで特に役立ちます。

ただし、BigDecimal用のコンストラクターを使用するときは注意してください。文字列コンストラクタは多くの場合非常に便利です。例えば、

BigDecimal onethird = new BigDecimal( "0.33333333333");

無限反復数を指定された精度で表すために、1/3の文字列表現を使用します。四捨五入エラーはJVM内のどこかにあるため、四捨五入エラーが実際の計算の大部分を妨げることはありません。しかしながら、私は個人的な経験から、四捨五入が見られるようになりました。 Oracleのドキュメントからわかるように、setScaleメソッドはこれらの点で重要です。

7
fishermanhat

あなたが計算を扱っているならば、あなたがどのように計算すべきか、そしてあなたがどの精度を使用すべきかについての法律があります。あなたが失敗した場合あなたは違法なことをするでしょう。唯一の真の理由は、10進数の場合のビット表現が正確ではないということです。バジルが簡単に言ったように、例は最もよい説明です。彼の例を補完するためだけに、これが起こるのです。

static void theDoubleProblem1() {
    double d1 = 0.3;
    double d2 = 0.2;
    System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));

    float f1 = 0.3f;
    float f2 = 0.2f;
    System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));

    BigDecimal bd1 = new BigDecimal("0.3");
    BigDecimal bd2 = new BigDecimal("0.2");
    System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}

出力:

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1

またそれがあります。

static void theDoubleProblem2() {
    double d1 = 10;
    double d2 = 3;
    System.out.println("Double:\t 10 / 3 = " + (d1 / d2));

    float f1 = 10f;
    float f2 = 3f;
    System.out.println("Float:\t 10 / 3 = " + (f1 / f2));

    // Exception! 
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}

私達に出力を与えます:

Double:  10 / 3 = 3.3333333333333335
Float:   10 / 3 = 3.3333333
Exception in thread "main" Java.lang.ArithmeticException: Non-terminating decimal expansion

しかし:

static void theDoubleProblem2() {
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}

出力があります:

BigDec:  10 / 3 = 3.3333 
4
jfajunior