Double vs. BigDecimal?


303

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


これをチェックしてください。stackoverflow.com/questions/322749/...
エスペンSchulstad

回答:


446

A BigDecimalは、数値を表す正確な方法です。A Doubleには一定の精度があります。さまざまな大きさの倍精度(たとえばd1=1000.0d2=0.0010.001を使用すると、大きさの差が非常に大きいため、合計すると全体がドロップされる可能性があります。これでBigDecimalは起こりません。

の欠点BigDecimalは、速度が遅くなることと、そのようにアルゴリズムをプログラムするのが少し難しくなることです(過負荷+ - */ないため)。

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

ここで説明するよりも説明がよくなるので、javadocを読むことをお勧めBigDecimalします:)


はい、在庫の価格を計算しているので、この場合はBigDecimalが役立つと思います。
Truong Ha

5
@Truong Ha:価格を扱う場合、BigDecimalを使用します。そして、それらをデータベースに保存する場合、同様のものが必要です。
エクストラ

98
「BigDecimalは数値を表す正確な方法である」と言うのは誤解を招くものです。1/3と1/7は、基数10の数体系(BigDecimal)または基数2の数体系(floatまたはdouble)では正確に表現できません。1/3は、base 3、base 6、base 9、base 12などで正確に表現でき、1/7は、base 7、base 14、base 21などで正確に表現できます。BigDecimalの利点は、任意の精度であることです。 10で発生する丸め誤差に人間が慣れていること
procrastinate_later 2013

3
それが遅くなることについて良い点、私はダブルスでなぜNetflixのリボンロードバランサコードのお得な情報を理解するのに役立ちますし、このような行があります:if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
michaelok

@extraneon「精度が必要な場合は使用するBigDecimal」と言うつもりだと思いますが、Doubleの方が「精度」が高くなります(桁数が多くなります)。
jspinella

164

私の英語は下手なので、ここで簡単な例を書いてみます。

    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

誰かがまだダブルを使いたいですか?;)


11
@eldjonそうではありません。この例を見てください。BigDecimal two = new BigDecimal( "2"); BigDecimal eight = new BigDecimal( "8"); System.out.println(two.divide(eight)); これにより、0.25が出力されます。
Ludvig W

4
doubles forevr:D
vach

それにもかかわらず、代わりにフロートを使用すると、その場合BigDecimalと同じ精度が得られますが、パフォーマンスは向上します
EliuX

3
@EliuX Floatは0.03-0.02で動作する可能性がありますが、他の値は依然として不正確です:System.out.println(0.003f - 0.002f);BigDecimalは正確です:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Martin

50

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

  • 任意の精度。BigIntegerと同様に、任意の精度とサイズの数を含めることができます
  • 基数2ではなく基数10

金額の計算にBigDecimalを使用する必要がある理由は、任意の数値を表すことができるということではなく、10進表記で表すことができ、通貨の世界の実質的にすべての数値を含むすべての数値を表すことができることです(1/3 $を転送することはありません)誰かへ)。


2
この回答は、BigDecimalをdoubleで使用する場合の違いと理由を本当に説明しています。パフォーマンスの問題は二次的です。
Vortex

これは100%真実ではありません。BigDecimalは "n * 10 ^ scale"であると書いた。Javaは負の数に対してのみそれを行います。したがって、「unscaledValue×10 ^ -scale」のようになります。正の数の場合、BigDecimalは「任意の精度の整数のスケールなしの値と32ビットの整数のスケール」で構成されますが、スケールは小数点の右側の桁数です。
NODのハンド

25

小数値などの小数値を書き留める1 / 7と、

1/7 = 0.142857142857142857142857142857142857142857...

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

小数部のある2進数のような数値1/10または1/1002進数として表される数値も、小数点の後に桁数が無限にあります。

1/10 = binary 0.0001100110011001100110011001100110...

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

BigDecimal一方、10進数(など)は、各10進数をそのまま格納します。つまり、10進数型は一般的な意味で2進浮動小数点または固定小数点型よりも正確ではありません(つまり1/7、精度を失わずに格納することはできません)が、10進数の桁数が有限である数値の方が正確です。多くの場合、お金の計算に当てはまります。

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


7

BigDecimalは、Oracleの任意精度数値ライブラリです。BigDecimalはJava言語の一部であり、金融​​から科学的なものまでさまざまなアプリケーションに役立ちます(そういうところです)。

特定の計算にdoubleを使用しても何も問題はありません。ただし、Math.Pi * Math.Pi / 6、つまり2の実引数(現在取り組んでいるプロジェクト)のリーマンゼータ関数の値を計算したいとします。浮動小数点除算は、丸め誤差の痛い問題を引き起こします。

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

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メソッドはこれらの点で重要です。


BigDecimalは、Javaの任意精度数値ライブラリ一部です。「社内」は、特にIBMによって作成されたため、このコンテキストでは意味がありません。
ローンの侯爵

@EJP:BigDecimalクラスを調べたところ、その一部のみがIBMによって作成されていることがわかりました。以下の著作権コメント: /* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
realPK 2017

7

計算を扱う場合、計算方法と使用する精度に関する法律があります。失敗した場合、違法な行為をすることになります。唯一の真の理由は、小数のケースのビット表現が正確でないことです。バジルが単純に述べたように、例が最良の説明です。彼の例を補足するために、これがどうなるかです:

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 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.