はじめに
いろいろと注意点の多い BigDecimal は、使おうとすると、注意点があるということ以外忘れてる。
- インスタンス生成は文字列から行うこと
- 比較は
equals()
でなくcompareTo()
を使うこと - Java8 より前は
stripTrailingZeros()
のバグに注意すること - Java1.5 以降では、丸めの指定は
RoundingMode
を使うこと
インスタンス化
文字列からインスタンス化
BigDecimal val = new BigDecimal("0.1");
double からインスタンス化
BigDecimal val = BigDecimal.valueOf(0.1);
以下のようにしてはいけない。
BigDecimal val = new BigDecimal(0.1);
2の補数で正確に表現できないため以下のようになってしまう。
val.toPlainString() // -> "0.1000000000000000055511151231257827021181583404541015625"
BigDecimal.valueOf()
は以下のように文字列を経由してインスタンス化してくれている。
public static BigDecimal valueOf(double val) { return new BigDecimal(Double.toString(val)); }
文字列からインスタンス化した場合は、文字列で指定した通りのスケールになる
BigDecimal val = new BigDecimal("0.10"); val.toPlainString() // -> 0.10
末尾のゼロを削除する
stripTrailingZeros()
を使う。
new BigDecimal("1.00").stripTrailingZeros(); // -> 1 new BigDecimal("0.10").stripTrailingZeros(); // -> 0.1 new BigDecimal("0.0").stripTrailingZeros(); // -> 0
JDK 7 では stripTrailingZeros()
にバグがあるため注意が必要(JDK 8で修正済み)。
JDK 7 では、以下のようなケースでゼロが削除されない。
new BigDecimal("0.0").stripTrailingZeros(); // -> 0.0
BigDecimal.stripTrailingZeros() has no effect on zero itself ("0.0")
BigDecimal を比較する
BigDecimal は値とスケールをそれぞれ管理しており、equals()
では双方の一致を見る。すなわち、値とスケールが両方とも同じでないと同値判定されない。
equals()
での比較は、大抵の場合望ましい動きにはならないため注意。
BigDecimal val = new BigDecimal("2.0").divide(new BigDecimal("2")); val.equals(BigDecimal.ONE); // -> false val.toString(); // -> 1.0 BigDecimal.ONE.toString(); // -> 1
数値としての一致を見るには compareTo()
を使う。
BigDecimal val = new BigDecimal("2.0").divide(new BigDecimal("2")); val.compareTo(BigDecimal.ONE); // -> 0
四則演算
スケールに応じて結果のスケールが異なるので注意が必要。
以下があった場合、
BigDecimal a = new BigDecimal("2.00"); BigDecimal b = new BigDecimal("2.0");
足し算 引き算のスケールは max(a.scale(), b.scale())
となる
a.add(b).toPlainString(); // -> "4.00" a.subtract(b).toPlainString(); // -> "0.00"
掛け算のスケールは this.scale() + multiplicand.scale()
となる。
a.multiply(b).toPlainString(); // -> "4.000" b.multiply(a).toPlainString(); // -> "4.000"
割り算の場合、優先スケールが this.scale() - divisor.scale()
となる。小数点以下が発生する場合はその必要数分のスケールとなる。
a.divide(b).toPlainString(); // -> "1.0" b.divide(a).toPlainString(); // -> "1"
小数点以下が無限となる場合は ArithmeticException となるためスケールや丸めを指定する。
丸め指定
int 定数の BigDecimal.ROUND_UP
ではなく、Java1.5 で追加された列挙 RoundingMode.UP
を使う。
new BigDecimal("1.234").setScale(0, RoundingMode.DOWN); // -> 1 new BigDecimal("12.34").setScale(1, RoundingMode.DOWN); // -> 12.3
BigDecimal.ONE.divide(new BigDecimal("3"), 2, RoundingMode.DOWN); // -> 0.33
RoundingMode
は以下がある
RoundingMode.UP
切り上げ(0から離れるように丸める)RoundingMode.DOWN
切り捨て(0に近づくように丸める)RoundingMode.CEILING
正の無限大に近づくように丸めるRoundingMode.FLOOR
負の無限大に近づくように丸めるRoundingMode.HALF_UP
四捨五入RoundingMode.HALF_DOWN
五捨六入(もっとも近い数字に丸める)RoundingMode.HALF_EVEN
もっとも近い数字に丸める(等距離の場合は偶数側)RoundingMode.UNNECESSARY
丸めを行わない(丸め発生時には例外)
「0から離れるように」とか「負の無限大に近づくように」とは以下のように考える
その他のメソッド
その他のよく使いがちなメソッド
メソッド | 説明 |
---|---|
abs() |
絶対値 |
divideAndRemainder(other) |
割り算の商と余り |
movePointLeft(n) |
× 10^-n スケールはmax(this.scale() + n, 0) |
movePointRight(n) |
× 10n スケールはmax(this.scale() - n, 0) |
scaleByPowerOfTen(n) |
× 10n スケールはthis.scale() - n |
negate() |
× -1 |
pow(n) |
n 乗 |
signum() |
符号(1/-1)の取得 |
longValue() |
long値の取得(longValueExact() は桁不足で例外をスロー) |
doubleValue() |
double値の取得 |
toPlainString() |
指数表記しない toString() |
BigDecimal.ZERO |
ゼロ |
BigDecimal.ONE |
1 |
BigDecimal.TEN |
10 |