java.math.BigDecimalは、Javaで任意精度の小数を表すクラス。
任意精度なので、メモリーの許す限りの桁数を保持できる。
|
BigDecimalのインスタンスは、newかvalueOf()で生成する。[2015-09-05]
メソッド | 例 | |
---|---|---|
new BigDecimal(String) |
new BigDecimal("123") |
|
new BigDecimal(double) |
new BigDecimal(123.4) |
|
new BigDecimal(BigInteger) |
new BigDecimal(new BigInteger("123")) |
|
new BigDecimal(int) |
1.5 | new BigDecimal(123) |
new BigDecimal(long) |
1.5 | new BigDecimal(123L) |
valueOf(long) |
BigDecimal.valueOf(123) |
|
valueOf(double) |
1.5 | BigDecimal.valueOf(123.4) |
定数 | 1.5 | BigDecimal.ZERO |
19 | BigDecimal.TWO [2022-09-21] |
BigDecimalを作るコンストラクターやメソッドには、引数にMathContextやスケール(小数点以下を保持する桁数)を指定するオーバーロードがあるものもある。
Stringを引数にとる場合、通常の数値表記(「123」や「123.4」とか)の他に、指数表記(「1e10」とか)も受け入れられる。
BigDecimalは不変オブジェクトなので、一旦初期化した値を変更することは出来ない。
(add, subtract, multiply, divideといった演算メソッドを呼ぶと、新しいBigDecimalを返す。(List#add()だと自らの中に要素を追加するのに、BigDecimal#add()は自らを変更せずに別のBigDecimalを返すので、違和感がある…。日付時刻だと、加算はaddでなくplusメソッドとなっている))
valueOf(long)を使ってBigDecimalを取得する場合、値が小さい(0〜10の)場合はキャッシュされているインスタンスを返す。
BigDecimal自身は10進数の固定小数を扱えるのだが、doubleを使って初期化する場合、doubleは2進数の浮動小数なので、BigDecimalに変換する際に小数点以下の値が変わってしまうことがある。
その為、小数を扱いたい場合は、doubleを使わず、Stringで初期化する方がよい。
BigDecimalにはStringに変換するメソッドがいくつか有る。[2015-09-05]
値 | コード | 結果 |
---|---|---|
BigDecimal d = BigDecimal.valueOf(123); |
d.toString() |
123 |
BigDecimal d = BigDecimal.valueOf(123.4); |
d.toString() |
123.4 |
BigDecimal d =
BigDecimal.valueOf(1000_0000_0000d); |
d.toString() |
1.0E+11 |
toStringメソッドだと、桁が大きい場合に指数表記になってしまう。
大抵の場合はtoPlainStringメソッドを使っておくのが良さそう。
小数点以下の末尾に0が続く場合、toString系メソッドを使うと、末尾の0が全て出力されてしまう。[2015-09-23]
この小数点以下の末尾0が不要な場合は、stripTrailingZerosメソッドを使って取り除くことが出来る。
値 | コード | 結果 |
---|---|---|
BigDecimal d = new BigDecimal("1.0100"); |
d.toString() |
1.0100 |
BigDecimal d = new BigDecimal("1.0100").stripTrailingZeros(); |
d.toString() |
1.01 |
BigDecimal同士の比較にはcompareTo()を使う。[2015-08-28]
equals()を使うと、スケールまで一致していないとtrueにならない。
コード例 | 結果 |
---|---|
BigDecimal z1 = new BigDecimal("0"); |
|
z1.equals(z2) |
false |
z1.compareTo(z2) == 0 |
true |
BigDecimal同士の割り算はdivide()で行う。
普通の割り算 |
BigDecimal n1 = BigDecimal.valueOf(123); BigDecimal n2 = BigDecimal.valueOf(10); BigDecimal n3 = n1.divide(n2); |
切り上げ・切り捨て・四捨五入等 |
BigDecimal n3 = n1.divide(n2, BigDecimal.ROUND_UP); BigDecimal n3 = n1.divide(n2, BigDecimal.ROUND_DOWN); BigDecimal n3 = n1.divide(n2, BigDecimal.ROUND_CEILING); BigDecimal n3 = n1.divide(n2, BigDecimal.ROUND_FLOOR); BigDecimal n3 = n1.divide(n2, BigDecimal.ROUND_HALF_UP); |
import java.math.RoundingMode; //列挙型 BigDecimal n3 = n1.divide(n2, RoundingMode.UP); BigDecimal n3 = n1.divide(n2, RoundingMode.DOWN); BigDecimal n3 = n1.divide(n2, RoundingMode.CEILING); BigDecimal n3 = n1.divide(n2, RoundingMode.FLOOR); BigDecimal n3 = n1.divide(n2, RoundingMode.HALF_UP); |
|
スケール(小数部の桁数)指定 |
BigDecimal n3 = n1.divide(n2, 3, RoundingMode.HALF_UP); |
ただし、結果の小数部が無限に続くときは例外が発生する。
BigDecimal r = BigDecimal.valueOf(1).divide(BigDecimal.valueOf(9));
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. at java.math.BigDecimal.divide(BigDecimal.java:1616) 〜
この場合は、切り上げ・切り捨て等の指定と小数部の桁数を指定すれば計算できる。
BigDecimal n1 = BigDecimal.valueOf(1); BigDecimal n2 = BigDecimal.valueOf(3); BigDecimal n3 = n1.divide(n2, 5, RoundingMode.HALF_UP);
0.33333
参考: lohlanさんのBigDecimal.divide() 使用時の注意
四則演算や比較等以外のメソッド。[2015-09-23]
メソッド名 | ver | 説明 | 例 | 結果 |
---|---|---|---|---|
pow | 1.5 | 累乗(n乗)を返す。 | BigDecimal.valueOf(2).pow(3) |
8 |
abs | 絶対値を返す。 | BigDecimal.valueOf(-1).abs() |
1 |
|
negate | 符号を反転した値を返す。 | BigDecimal.valueOf(1).negate() |
-1 |
|
plus | 1.5 | 自分自身を返す。(negateと対称になるメソッド) | BigDecimal.valueOf(1).plus() |
1 |
signum | 符号(を表す数)を返す。 正の場合は1、0の場合は0、負の場合は-1。 |
BigDecimal.valueOf(2).signum() |
1 |
|
scale | スケールを返す。 | BigDecimal.valueOf(0.01).scale() |
2 |
|
precision | 1.5 | 精度を返す。 | BigDecimal.valueOf(100.01).precision() |
5 |
unscaledValue | 1.2 | スケール無しの値(BigInteger)を返す。 | BigDecimal.valueOf(100.01).unscaledValue() |
10001 |
round | 1.5 | 丸めた(四捨五入とかを行った)値を返す。 | MathContext mc = new MathContext(4,
RoundingMode.HALF_UP); |
|
setScale | スケールを指定した新しい値を返す。 | |||
movePointLeft | 小数点をn桁左へシフトした値を返す。(10-n倍) | BigDecimal.valueOf(10.01).movePointLeft(1) |
1.001 |
|
movePointRight | 小数点をn桁右へシフトした値を返す。(10n倍) | BigDecimal.valueOf(10.01).movePointRight(1) |
100.1 |
|
scaleByPowerOfTen | 1.5 | 10n倍した値を返す。 | BigDecimal.valueOf(10.01).scaleByPowerOfTen(1) |
100.1 |
stripTrailingZeros | 1.5 | 小数点以下の末尾の0を取り除いた値を返す。 | new BigDecimal("1.0100").toPlainString() |
1.0100 |
min | 自分自身と引数のうち、小さい方を返す。 | BigDecimal.valueOf(2).min(BigDecimal.valueOf(3)) |
2 |
|
max | 自分自身と引数のうち、大きい方を返す。 | BigDecimal.valueOf(2).max(BigDecimal.valueOf(3)) |
3 |
|
ulp | 最小単位の数を返す。 小数以下が無い(スケール=0の)場合は1が返る。 (この値を加算すれば、(スケールが同じ範囲の)“自分の次の数”が得られる) |
BigDecimal.valueOf(0.03).ulp() |
0.01 |
BigDecimalは(メモリーの許す限り)どんな桁数でも保持できるが、long値の範囲内と範囲外では演算速度が異なる。[2014-09-28]
BigDecimal d = new BigDecimal(0).add(BigDecimal.ONE); // BigDecimal d = new BigDecimal(Long.MAX_VALUE).add(BigDecimal.ONE); long s = System.currentTimeMillis(); for (int i = 0; i < 1_0000_0000; i++) { // 1億回 d.add(BigDecimal.ONE); } long e = System.currentTimeMillis(); System.out.println(e - s);
初期値 | 1 | Long.MAX_VALUE-1 | Long.MAX_VALUE | Long.MAX_VALUE+1 | 1 | Long.MAX_VALUE+1 |
---|---|---|---|---|---|---|
加算値 | 1 | 1 | 1 | 1 | Long.MAX_VALUE+1 | Long.MAX_VALUE+1 |
時間[ms] | 350〜450 | 398〜505 | 4131〜4726 | 3430〜3978 | 3571〜3734 | 4314〜4874 |
平均[ms] | 410 | 444 | 4503 | 3674 | 3650 | 4501 |
備考 | 同じ(誤差の範囲内) | 同じ(誤差の範囲内) |
初期値 | 2 | Long.MAX_VALUE+1 | 2 | Long.MAX_VALUE+1 |
---|---|---|---|---|
乗算値 | 2 | 2 | Long.MAX_VALUE+1 | Long.MAX_VALUE+1 |
時間[ms] | 379〜510 | 3702〜4431 | 3504〜4066 | 4150〜4705 |
平均[ms] | 448 | 4176 | 3749 | 4441 |
値がlongの範囲に収まっている場合とそうでない場合は10倍くらい速度が違う。
BigDecimal内部での実装方法が、long値に収まる範囲とそうでない範囲で異なるらしい。