当サイトは、アフィリエイト広告を利用しています

BigDecimalで数値を扱う方法

作成日:2022月10月22日
更新日:2023年06月24日

javaで精密な数値の計算などを行う時はBigDecimalをよく
使うので基本的な使い方(下記)をまとめておく。

  • BigDecimalの生成方法
  • 四則演算
  • 丸め処理(四捨五入)
  • 0かどうか判定
  • 比較

BigDecimalの用途

javaで少数の計算を行う時、丸め誤差をでないように
小数点以下を正確に扱うためにBigDecimalを使う。
今までの業務経験でも金額などの正確な数値を扱う必要がある場合は
BigDecimalが使われることが多かった気がします。

丸め誤差が発生するパターン

下記のような減算をdobule型で行った場合
誤差が発生する

double型で計算
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
double x = 1.0;
double y = 0.9;
System.out.println(x-y);
}
}
// 0.09999999999999998

0.9のように2進数で表すと循環少数になってしますような少数は
誤差が発生してしまう。
コンピュータでは有限桁数の数しか扱えないので、適当な値に丸められ誤差が生じる。

BigDecimalを使って計算してみると

BigDecimalで計算
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
BigDecimal x = new BigDecimal("1.0");
BigDecimal y = new BigDecimal("0.9");
System.out.println(x.subtract(y));
}
}
// 0.1

BigDecimalを使えば正確な計算結果が得られる。

BigDecimalの初期化方法

BigDecimalの初期化方法をまとめる。
初期化の結論を先にいうと、コンストラクタでStringを引数にして初期化する
のが一番安全に初期化できる。

定数で初期化

BigDecimalの定数を使って初期化する。

定数
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
BigDecimal x = BigDecimal.ZERO;
System.out.println(x);
}
}
//実行結果
//0

合計値を保持する変数として使う場合などは定数を使うことがある

valueOfメソッドを使用して初期化

valueOfメソッドを使って初期化することもできる

valueOfメソッド
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
BigDecimal z = BigDecimal.valueOf(0.0);
System.out.println(z);
}
}
//実行結果
//0.0

正直、あまり使わない。というか使ったことがない。

コンストラクタで初期化

一番使うパターン。
String型やint型tやdouble型を引数にして初期化できる

コンストラクタで初期化
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String a = "0.0";
int b = 0;
double c = 0.0;
BigDecimal y1 = new BigDecimal(a);
BigDecimal y2 = new BigDecimal(b);
BigDecimal y3 = new BigDecimal(c);
System.out.println(y1);
System.out.println(y2);
System.out.println(y3);
}
}
// 0.0
// 0
// 0

BigDecimal型は文字列で初期化する方がいい

コンストラクタで初期化する場合でも丸め誤差は発生する
下記のように循環少数になるような値を設定すると

コンストラクタで初期化
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String a = "0.9";
double c = 0.9;
BigDecimal y1 = new BigDecimal(a);
BigDecimal y3 = new BigDecimal(c);
System.out.println(y1);
System.out.println(y3);
}
}
//実行結果
// 0.9
// 0.90000000000000002220446049250313080847263336181640625

こんな感じになってしまうので...

nullと""の値を引数に設定した場合について

コンストラクタの引数にnullや""を設定してしまうとExceptionになるので
StringUtis.isEmpty()メソッドなどを使って対応する必要がある。

nullとBlank対応
BigDecimal x = new BigDecimal(StringUtils.isEmpty(val)?BigDecimal.ZERO:val);

オブジェクトのString型のメンバの値をゲッターで取得して計算する時などは
対応する必要がある。

丸め処理をする

BigDecimalの値を丸める方法をまとめる。
丸めるにはsetScaleメソッドを使用する。

基本構文

setScale
BigDecimal bd = bd.setScale(少数以下の桁数, 丸め処理方法);
  • 第一引数には少数以下の桁数を設定
  • 第二引数には丸め処理方法を設定

丸め処理サンプル

下記で実際に丸目処理を行っているサンプルを書いてみる

setScaleサンプル
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr = "1.2345";
BigDecimal bg = new BigDecimal(bgStr);
//少数桁なしで四捨五入
BigDecimal bg1 = new BigDecimal(bgStr).setScale(0,BigDecimal.ROUND_HALF_UP);
//少数1桁表示で切り捨て
BigDecimal bg2 = new BigDecimal(bgStr).setScale(1,BigDecimal.ROUND_DOWN);
//少数2桁表示なしで切り上げ
BigDecimal bg3 = new BigDecimal(bgStr).setScale(2,BigDecimal.ROUND_UP);
System.out.println("setScaleなし: " + bg);
System.out.println("四捨五入 : " + bg1);
System.out.println("切り捨て : " + bg2);
System.out.println("切り上げ : " + bg3);
}
}
//実行結果
// setScaleなし: 1.2345
// 四捨五入 : 1
// 切り捨て : 1.2
// 切り上げ : 1.24
  • 丸め処理方法はRoundingModeで指定する。

BigDecimalの四則演算

BigDecimalの四則演算の方法をまとめる。

加算

加算はaddメソッドを使う

add
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr1 = "1.0";
String bgStr2 = "0.9";
BigDecimal bg1 = new BigDecimal(bgStr1);
BigDecimal bg2 = new BigDecimal(bgStr2);
BigDecimal result1 = bg1.add(bg2);
System.out.println(result1);
}
}
//実行結果
//1.9

減算

減算にはsubtractを使う

subtract
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr1 = "1.0";
String bgStr2 = "0.9";
BigDecimal bg1 = new BigDecimal(bgStr1);
BigDecimal bg2 = new BigDecimal(bgStr2);
BigDecimal result2 = bg1.subtract(bg2);
System.out.println(result2);
}
}
//実行結果
//0.1

乗算

乗算にはmultiplyを使う

multiply
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr1 = "1.0";
String bgStr2 = "0.9";
BigDecimal bg1 = new BigDecimal(bgStr1);
BigDecimal bg2 = new BigDecimal(bgStr2);
BigDecimal result3 = bg1.multiply(bg2);
System.out.println(result3);
}
}

除算

除算にはdivideを使う

divide
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr1 = "1.0";
String bgStr2 = "0.9";
BigDecimal bg1 = new BigDecimal(bgStr1);
BigDecimal bg2 = new BigDecimal(bgStr2);
BigDecimal result4 = bg1.divide(bg2);
System.out.println(result4);
}
}
//実行結果
//java.lang.ArithmeticException

除算の場合結果が無限小数(3.3333~)となる場合、ArithmeticException
が発生するので、

  • 第二引数に表示する少数桁数、
  • 第三引数に丸め処理モード

を設定することができる。

divide
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr1 = "1.0";
String bgStr2 = "0.9";
BigDecimal bg1 = new BigDecimal(bgStr1);
BigDecimal bg2 = new BigDecimal(bgStr2);
BigDecimal result4 = bg1.divide(bg2,2,BigDecimal.ROUND_HALF_UP);
System.out.println(result4);
}
}
//実行結果
//1.11

丸め処理を設定していなくても無限少数にならない場合はArithmeticExceptionは
発生しないが、設定しておくほうが無難!

BigDecimalか0かどうか判定する方法

BigDecimalが0かどうか判定するにはsignumメソッドを使う

signum
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
String bgStr1 = "0.0";
String bgStr2 = "0.9";
BigDecimal bg1 = new BigDecimal(bgStr1);
System.out.println(bg1.signum()== 0);
}
}
// 実行結果
// true
  • 0と比較する時のみsignumが使える

BigDecimal同士を比較する

BigDecimal同士を比較する場合はcompareToメソッドを使用する
compareToメソッドは

  • 左辺 < 右辺 ならば、 -1を返す
  • 左辺 = 右辺 ならば、 0を返す
  • 左辺 > 右辺 ならば、 1を返す
java
import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) throws Exception {
BigDecimal bg1 = new BigDecimal("1.0");
BigDecimal bg2 = new BigDecimal("5.0");
BigDecimal bg3 = new BigDecimal("5.0");
int result1 = bg1.compareTo(bg2);
int result2 = bg2.compareTo(bg2);
int result3 = bg2.compareTo(bg1);
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
}
}
// 実行結果
// -1
// 0
// 1
  • compareToメソッドの戻り値はintで帰ってくる

まとめ

実際の業務でも少数がある値や正確な計算をするときにBigDecimalは
よく使うので(逆にdoubleやlongはあまりみない)基本的な使い方と
注意点をまとめた。
注意することは初期化時にnullや""が設定されないようにすることと
割り算を行う時は必ずsetScaleをつけるようにすることぐらいかと思います。

BigDecimal型の変数をstreamのreduceで集計する方法については
下記の記事で紹介しています

参考

関連記事

新着記事

タグ一覧
top