当サイトは、アフィリエイト広告を利用しています
javaで精密な数値の計算などを行う時はBigDecimalをよく
使うので基本的な使い方(下記)をまとめておく。
javaで少数の計算を行う時、丸め誤差をでないように
小数点以下を正確に扱うためにBigDecimalを使う。
今までの業務経験でも金額などの正確な数値を扱う必要がある場合は
BigDecimalが使われることが多かった気がします。
下記のような減算をdobule型で行った場合
誤差が発生する
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を使って計算してみると
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の初期化方法をまとめる。
初期化の結論を先にいうと、コンストラクタで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メソッドを使って初期化することもできる
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
コンストラクタで初期化する場合でも丸め誤差は発生する
下記のように循環少数になるような値を設定すると
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や""を設定してしまうとExceptionになるので
StringUtis.isEmpty()メソッドなどを使って対応する必要がある。
BigDecimal x = new BigDecimal(StringUtils.isEmpty(val)?BigDecimal.ZERO:val);
オブジェクトのString型のメンバの値をゲッターで取得して計算する時などは
対応する必要がある。
BigDecimalの値を丸める方法をまとめる。
丸めるにはsetScaleメソッドを使用する。
BigDecimal bd = bd.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
BigDecimalの四則演算の方法をまとめる。
加算は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を使う
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を使う
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を使う
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
が発生するので、
を設定することができる。
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かどうか判定するには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
BigDecimal同士を比較する場合はcompareToメソッドを使用する
compareToメソッドは
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
実際の業務でも少数がある値や正確な計算をするときにBigDecimalは
よく使うので(逆にdoubleやlongはあまりみない)基本的な使い方と
注意点をまとめた。
注意することは初期化時にnullや""が設定されないようにすることと
割り算を行う時は必ずsetScaleをつけるようにすることぐらいかと思います。
BigDecimal型の変数をstreamのreduceで集計する方法については
下記の記事で紹介しています