Java BigDecimal チートシート


はじめに

いろいろと注意点の多い 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