私はいくつかの浮動小数点変数を計算する必要があり、同僚はそれがより正確になるのでBigDecimal
代わりに使用することを提案します。double
しかし、私はそれが何であるか、そしてどのようにそれを最大限に活用するかを知りたいBigDecimal
ですか?
6 に答える
ABigDecimal
は数値を表す正確な方法です。ADouble
には一定の精度があります。さまざまな大きさのdouble(たとえばd1=1000.0
、d2=0.001
)を0.001
使用すると、大きさの差が非常に大きいため、合計すると一緒にドロップされる可能性があります。これBigDecimal
では起こりません。
の欠点はBigDecimal
、速度が遅く、そのようにアルゴリズムをプログラムするのが少し難しいことです(過負荷+
-
*
で/
はないため)。
お金を扱っている場合、または精度が必須の場合は、を使用してBigDecimal
ください。そうでなければDoubles
、十分に良い傾向があります。
私はここで私がするよりも物事をよりよく説明しているので、のjavadocを読むことをお勧めBigDecimal
します:)
私の英語は上手ではないので、ここで簡単な例を書いてみましょう。
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
誰かがまだダブルを使いたいですか?;)
doubleとの主な違いは2つあります。
- 任意精度、BigIntegerと同様に、任意の精度とサイズを含めることができます(doubleのビット数は固定されています)。
- 基数2ではなく基数10、BigDecimalは
n*10^-scale
、nが任意の大きな符号付き整数であり、スケールは小数点を左または右に移動する桁数と考えることができます。
BigDecimalが任意の数を表すことができると言うのはまだ真実ではありません。しかし、金銭計算にBigDecimalを使用する必要がある2つの理由は次のとおりです。
- これは、10進数で表すことができ、通貨の世界のほぼすべての数値を含むすべての数値を表すことができます(1/3 $を誰かに転送することはありません)。
- 累積誤差を回避するために精度を制御できます。を使用する
double
と、値の大きさが増加するにつれて、その精度が低下し、結果に重大なエラーが発生する可能性があります。
1 / 7
10進値のように小数値を書き留めると、次のようになります。
1/7 = 0.142857142857142857142857142857142857142857...
の無限のシーケンスで142857
。記述できる桁数は有限であるため、必然的に丸め(または切り捨て)エラーが発生します。
小数部のある2進数のような1/10
、または1/100
2進数として表される数値も、小数点以下の桁数が無限になります。
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
値を2進数として格納するため、算術演算を行わずに、10進数を2進数に変換するだけでエラーが発生する可能性があります。
一方、 10進数(のようなBigDecimal
)は、各10進数をそのまま格納します(2進数でコード化されていますが、各10進数はそれ自体で)。これは、10進数型は、一般的な意味で2進浮動小数点型または固定小数点型よりも正確ではないことを意味します(つまり1/7
、精度を失うことなく格納することはできません)が、10進数の桁数が有限である数値の場合はより正確です。お金の計算にはよくあることです。
JavaBigDecimal
には、使用可能なメモリによってのみ制限される、小数点の両側に任意の(ただし有限の)桁数を含めることができるという追加の利点があります。
計算を扱う場合、計算方法と使用する精度に関する法則があります。あなたが失敗した場合、あなたは違法なことをするでしょう。唯一の本当の理由は、小数点以下の桁数のビット表現が正確ではないということです。バジルが簡単に言うと、例が最良の説明です。彼の例を補足するために、次のようにします。
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
BigDecimalは、Oracleの任意精度の数値ライブラリです。BigDecimalはJava言語の一部であり、金融から科学に至るまでのさまざまなアプリケーションに役立ちます(これは一種の目的です)。
特定の計算にdoubleを使用しても問題はありません。ただし、Math.Pi * Math.Pi / 6、つまり2の実際の引数(現在取り組んでいるプロジェクト)のリーマンゼータ関数の値を計算したいとします。浮動小数点除算は、丸め誤差の問題を引き起こします。
一方、BigDecimalには、式を任意精度で計算するための多くのオプションが含まれています。以下のOracleドキュメントで説明されているように、add、multiply、およびdividメソッドは、BigDecimal Java Worldの+、*、および/の「代わりになります」。
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
compareToメソッドは、whileループとforループで特に役立ちます。
ただし、BigDecimalのコンストラクターの使用には注意してください。文字列コンストラクタは、多くの場合非常に便利です。たとえば、コード
BigDecimal 1/3 = new BigDecimal( "0.33333333333");
1/3の文字列表現を利用して、指定された精度で無限に繰り返される数値を表します。丸め誤差は、JVMの奥深くにある可能性が高いため、丸め誤差によって実際の計算のほとんどが妨げられることはありません。しかし、私は個人的な経験から、四捨五入が忍び寄るのを見てきました。Oracleのドキュメントからわかるように、setScaleメソッドはこれらの点で重要です。