S-JIS[2012-03-09/2015-09-23] 変更履歴

BigDecimal

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
BigDecimal.ONE
BigDecimal.TEN

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()
d.toEngineeringString()
d.toPlainString()
123
123
123
BigDecimal d = BigDecimal.valueOf(123.4); d.toString()
d.toEngineeringString()
d.toPlainString()
123.4
123.4
123.4
BigDecimal d = BigDecimal.valueOf(1000_0000_0000d); d.toString()
d.toEngineeringString()
d.toPlainString()
1.0E+11
100E+9
100000000000

toStringメソッドだと、桁が大きい場合に指数表記になってしまう。
大抵の場合はtoPlainStringメソッドを使っておくのが良さそう。


小数点以下の末尾に0が続く場合、toString系メソッドを使うと、末尾の0が全て出力されてしまう。[2015-09-23]
この小数点以下の末尾0が不要な場合は、stripTrailingZerosメソッドを使って取り除くことが出来る。

コード 結果
BigDecimal d = new BigDecimal("1.0100"); d.toString()
d.toEngineeringString()
d.toPlainString()
1.0100
1.0100
1.0100
BigDecimal d = new BigDecimal("1.0100").stripTrailingZeros(); d.toString()
d.toEngineeringString()
d.toPlainString()
1.01
1.01
1.01

比較

BigDecimal同士の比較にはcompareTo()を使う。[2015-08-28]

equals()を使うと、スケールまで一致していないとtrueにならない。

コード例 結果
BigDecimal z1 = new BigDecimal("0");
BigDecimal z2 = new BigDecimal("0.0");
 
z1.equals(z2)
z1.equals(BigDecimal.ZERO)
z2.equals(BigDecimal.ZERO)
false
true
false
z1.compareTo(z2) == 0
z1.compareTo(BigDecimal.ZERO) == 0
z2.compareTo(BigDecimal.ZERO) == 0
true
true
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()
BigDecimal.valueOf(-1).negate()
-1
1
plus 1.5 自分自身を返す。(negateと対称になるメソッド) BigDecimal.valueOf(1).plus()
BigDecimal.valueOf(-1).plus()
1
-1
signum   符号(を表す数)を返す。
正の場合は1、0の場合は0、負の場合は-1。
BigDecimal.valueOf(2).signum()
BigDecimal.valueOf(0).signum()
BigDecimal.valueOf(-2).signum()
1
0
-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);
BigDecimal.valueOf(100.05).round(mc)
BigDecimal.valueOf(100.04).round(mc)

100.1
100.0
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()
new BigDecimal("1.0100").stripTrailingZeros().toPlainString()
1.0100
1.01
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);
Windows7・Java8で1億回 演算した実行時間
初期値 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値に収まる範囲とそうでない範囲で異なるらしい。


型へ戻る / 新機能へ戻る / Java目次へ戻る
メールの送信先:ひしだま