【Java入門 実用編】 BigDecimal クラスの利用(1)
2025.08.01
前回の記事では、Javaで基本的な数学的計算や算術演算を行うための標準クラスである Math クラスについてご紹介しました。当記事からは Java において long や double の範囲を超える数値や、10進数の小数の厳密な計算に利用される BigDecimal クラスについて、概要や主な利用方法をサンプルコードとともにご紹介していきます。
java.math.BigDecimal クラス
BigDecimal クラスとは
前回 Math クラスに関する記事でご紹介したように、Javaでは一般的に int、long、float、doubleといったプリミティブ型で数値を扱います。しかし、特にfloatやdoubleのような浮動小数点数には、コンピュータ内部での表現方法の特性上、わずかな丸め誤差が生じるという課題がありました。
このためこれらの型では、非常に大きな数値や、金融などの分野で求められる厳密な小数の計算を行うことができません。
このような課題を解決し、より正確な精度で数値を扱うために用意されたクラスが、java.math.BigDecimalクラスです。BigDecimalクラスは、数値を内部的に文字列として保持することで、小数点以下の桁数を制限することなく、極めて正確な計算を可能にします。
BigDecimal クラスのインポートと利用
インポート
BigDecimal クラスは java.math パッケージ内のクラスであるため、利用時はインポート宣言が必要です。
import java.math.BigDecimal;
BigDecimalの利用
BigDecimal クラスを利用するには、主に2つの方法があります。1つはクラスが提供する静的メソッド(BigDecimal.valueOf メソッド)を利用してインスタンスを得る方法です。これはlong(整数)およびdouble(小数)に対応しています。
もう1つは一般的なクラスと同じように、コンストラクタを用いてインスタンスを生成する方法です。コンストラクタはlongやdoubleといった数値の他に、同じ java.math パッケージ内の整数用のクラス BigInteger や、数値を表す文字列、文字の配列を引数として実行することができます。
インスタンスの生成方法 | 書式 | 対応している引数の型 |
ファクトリーメソッド | BigDecimal.valueOf( xxx ); | long double |
コンストラクタを利用 | new BigDecimal( xxx ); | int long double java.math.BigInteger 文字列(数値を表す文字列) char配列 |
コード例:
// 100(long値)
BigDecimal bdVal1 = BigDecimal.valueOf(100);
// 1.5(double値)
BigDecimal bdVal2 = BigDecimal.valueOf(1.5);
// 100(int値)
BigDecimal bdNew1 = new BigDecimal(100);
// 1.5(文字列値)
BigDecimal bdNew2 = new BigDecimal("1.5");
// 123(char配列{'\\','1','2','3','-'} の1番目から3文字)
BigDecimal bdNew3 = new BigDecimal(new char[] {'\\','1','2','3','-'}, 1, 3);
BigDecimal.valueOf メソッドとキャッシュ
BigDecimal クラスで小さい整数(0 ~ 10)を繰り返し扱う場合は、valueOf メソッドを利用することで BigDecimal クラスのキャッシュを利用することができます。
このため、特に繰り返し整数を BigDecimal クラスで扱う場合は、new キーワードでインスタンスを生成せず、valueOf メソッドを利用することでメモリの使用量を抑え、パフォーマンスの向上につながる場合があります。
BigDecimal bdNew1 = new BigDecimal(1);
BigDecimal bdNew2 = new BigDecimal(1);
// false(別のインスタンス)
System.out.println(bdNew1 == bdNew2);
BigDecimal bdVal1 = BigDecimal.valueOf(1);
BigDecimal bdVal2 = BigDecimal.valueOf(1);
// true(キャッシュされている同一のインスタンス)
System.out.println(bdVal1 == bdVal2);
BigDecimal クラスの定数
BigDecimalクラスには、よく利用される値としてキャッシュ以外に定数が定義されています。
下表の値の BigDecimal インスタンスを利用する際は、コンストラクタやvalueOf メソッドを利用せずとも、定数に定義されているインスタンスを利用することができます。
定数 | BigDecimal.ZERO | BigDecimal.ONE | BigDecimal.TWO | BigDecimal.TEN |
値 | 0 | 1 | 2 | 10 |
小数値を扱う場合の注意点
小数値の BigDecimal インスタンスを作成する場合は、double 値を引数としたコンストラクタ および valueOf メソッドが準備されていますが、コンストラクタにそのまま double の変数を引き渡した場合に、浮動小数点数の僅かな誤差が BigDecimal の値として取り込まれる場合があります。(下記サンプルコード参照)
これを避けるためにも、特段の理由がない限り、BigDecimal の利用時には基本的に valueOf メソッドを利用することを推奨します。
double d1 = 0.3d;
double d2 = 0.7d;
/* double 型を引数とするコンストラクタを利用 */
// 0.299999999999999988897769753748434595763683319091796875
System.out.println(new BigDecimal(d1));
// 0.6999999999999999555910790149937383830547332763671875
System.out.println(new BigDecimal(d2));
/* double 型を引数とする valueOf メソッドを利用 */
// 0.3
System.out.println(BigDecimal.valueOf(d1));
// 0.7
System.out.println(BigDecimal.valueOf(d2));
/* 文字列を引数とするコンストラクタをString.valueOf メソッドとともに利用 */
// 0.3
System.out.println(new BigDecimal(String.valueOf(d1)));
// 0.7
System.out.println(new BigDecimal(String.valueOf(d2)));
valueOf メソッドを利用する場合と、数値を表す文字列を利用してコンストラクタを呼び出す場合は内部的に同一の動作となりますので、基本的に処理性能などの差はありません。
実際に BigDecimal クラスのソースを確認すると、valueOf メソッドの引数に double 値を引き渡した場合は、Double.toString メソッドを利用して、文字列を引数とする BigDecimal クラスのコンストラクタを呼び出していることがわかります。
// BigDecimal.java より抜粋
public static BigDecimal valueOf(double val) {
return new BigDecimal(Double.toString(val));
}
精度とスケール
BigDecimal クラスは、精度(precision)とスケール(scale)のプロパティを持ちます。精度は何桁までの値を保持するか(例えば「1230」を「1.23+3E」と表記する場合の「1.23」の部分の桁数)を指し、スケールは小数値の場合に小数点以下の何桁までを保持するかを指定します。精度が 0 の場合は、全ての桁を省かずに保持します。
BigDecimal では new キーワードでのインスタンスの生成時や各種メソッドの実行時に、MathContext クラスを利用して精度を指定できる場合があります。但し、精度を指定することで失われる桁がある場合、変換後のインスタンスではその部分のデータが失われることに注意が必要です。
BigDecimal bd = new BigDecimal(12345, new MathContext(3));
// 1.23E+4 となり、下二けた(45)の部分のデータは失われる。
System.out.println(bd);
MathContext クラスでは精度のほかに、失われる桁に対しての処理(切り上げ、切り捨てなど)を指定することができます。この内容については長くなりますので、次回以降の記事で別途説明します。
BigDecimal のインスタンスは「不変」
BigDecimal クラスを利用するにあたって注意が必要なの点として挙げられるのは、BigDecimal のインスタンスは「不変」であるということです。これは、一度生成されたインスタンスに対して、後から変更を加えることはできないということを示します。
下記で紹介するメソッド、例えば四則演算(add、subtract などのメソッド)やスケールの変更(setScale メソッド)を呼び出した場合、メソッドから返されるのは結果を表す別の BigDecimal のインスタンスであり、メソッドを呼び出したインスタンス自体は一切変更されていないことに注意が必要です。
BigDecimal bd1 = BigDecimal.valueOf(1);
// 変数 bd1 の値は 1 のまま
bd1.add(BigDecimal.valueOf(2));
System.out.println(bd1);
// 変数 bd1 に加算の実行結果を反映したい場合は代入する必要がある
bd1 = bd1.add(BigDecimal.valueOf(2));
System.out.println(bd1);
四則演算
BigDecimal はクラス型であるため、+ や - といった演算子での計算を行うことはできません。そのため、BigDecimal クラスには、四則演算のためのメソッドが準備されています。
これらのメソッドは、BigDecimal 同士での演算を行うことができます。プリミティブ型の変数との演算を行いたい場合は、BigDecimal の変数に変換してから行う必要があることに注意してください。
加算(add メソッド)
BigDecimal の加算を行うには add メソッドを利用します。add メソッドでは、引数に渡された BigDecimal との和を表す BigDecimal インスタンスを返します。
add メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
メソッド呼び出し元と引数のスケールが異なる場合、返される値はこれらのうち大きな方のスケールのBigDecimal として返されます。
BigDecimal bd1 = BigDecimal.valueOf(1);
BigDecimal bd2 = BigDecimal.valueOf(1.5);
BigDecimal bd3 = BigDecimal.valueOf(0.625);
BigDecimal bd4 = BigDecimal.valueOf(0.125);
// 2.5(整数と小数 → 小数側のスケール)
System.out.println(bd1.add(bd2));
// 1.625(小数と小数 → スケールが大きいほうのスケール)
System.out.println(bd2.add(bd3));
// 0.750(小数と小数の場合、切りあがってもスケールは変わらない)
System.out.println(bd3.add(bd4));
減算(subtract メソッド)
BigDecimal の減算を行うには subtract メソッドを利用します。subtract メソッドでは、引数に渡された BigDecimal との差を表す BigDecimal インスタンスを返します。
subtract メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
add メソッドと同様に、メソッド呼び出し元と引数のスケールが異なる場合、返される値はこれらのうち大きな方のスケールのBigDecimal として返されます。
BigDecimal bd1 = BigDecimal.valueOf(1);
BigDecimal bd2 = BigDecimal.valueOf(1.5);
BigDecimal bd3 = BigDecimal.valueOf(0.625);
BigDecimal bd4 = BigDecimal.valueOf(0.125);
// -0.5(整数と小数 → 小数側のスケール)
System.out.println(bd1.subtract(bd2));
// 0.875(小数と小数 → スケールが大きいほうのスケール)
System.out.println(bd2.subtract(bd3));
// 0.500(小数と小数の場合、切りあがってもスケールは変わらない)
System.out.println(bd3.subtract(bd4));
乗算(multiply メソッド)
BigDecimal の減算を行うには multiply メソッドを利用します。multiply メソッドでは、引数に渡された BigDecimal との積を表す BigDecimal インスタンスを返します。
multiply メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
返却される BigDecimal のスケールは、メソッド呼び出し元と引数のスケールの和となります。これは、小数同士での乗算に対応するためです。スケールが0の整数同士で乗算を行う場合は、スケールが0の整数を示す BigDecimal インスタンスが返却されます。
BigDecimal bd1 = BigDecimal.valueOf(2);
BigDecimal bd2 = BigDecimal.valueOf(1.5);
BigDecimal bd3 = BigDecimal.valueOf(0.05);
BigDecimal bd4 = BigDecimal.valueOf(0.004);
// 3.0(スケールの大きさは2つのインスタンスのスケールの合計)
System.out.println(bd1.multiply(bd2));
// 0.075(スケールの大きさは2つのインスタンスのスケールの合計)
System.out.println(bd2.multiply(bd3));
// 0.00020(切りあがってもスケールの大きさはスケールの合計)
System.out.println(bd3.multiply(bd4));
除算・剰余
除算(divide メソッド)
BigDecimal の除算を行うには divide メソッドを利用します。divide メソッドでは、引数に渡された BigDecimal での除算の結果を表す BigDecimal インスタンスを返します。
divide メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
divide メソッドでは、割り切れる限りスケールは自動的に拡張されますが、商の値が10の倍数となった場合は、最小の精度が自動的に割り当てられることに注意してください。
BigDecimal bd1 = BigDecimal.valueOf(10);
BigDecimal bd2 = BigDecimal.valueOf(1.5);
BigDecimal bd3 = BigDecimal.valueOf(0.05);
BigDecimal bd4 = BigDecimal.valueOf(0.004);
// 0.75
System.out.println(bd2.divide(bd1));
// 3E+1(30)
System.out.println(bd2.divide(bd3));
// 12.5
System.out.println(bd3.divide(bd4));
// 2.5E+3(2500)
System.out.println(bd1.divide(bd4));
divide メソッドではゼロ除算を行った場合や、除算の結果が割り切れずに循環小数になるような場合は、例外(ArithmeticException)が発生します。割り切れない可能性がある場合は、スケールや RoundingMode(切り上げ、切り捨てなど)を指定できるオーバーロードメソッドを利用するか、後述のメソッドで商だけを取得するなどの対応を行う必要があります。
BigDecimal bd1 = BigDecimal.valueOf(10);
BigDecimal bd2 = BigDecimal.valueOf(1.5);
// 割り切れない場合は例外(ArithmeticException)が発生
System.out.println(bd1.divide(bd2));
商(divideToIntegralValue メソッド)
divideToIntegralValue メソッドは、引数の BigDecimal で除算を行った際の商(整数部)を示す BigDecimal インスタンスを返します。
divideToIntegralValue メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
divideToIntegralValue メソッドでは、ゼロ除算を除き割り切れない場合でも例外は発生しません。
また戻り値は整数値ですが、スケールはメソッドを呼び出す BigDecimal インスタンスのスケールから、引数の BigDecimal インスタンスのスケールを引いた値(マイナスになる場合は0)となります。また、これらのインスタンスは小数値であっても問題ありません。
また商のスケールが 0 である場合に限り、divide メソッド同様に商が 10 の倍数の場合は最小の精度が割り当てられます。
BigDecimal bd1 = BigDecimal.valueOf(10.001);
BigDecimal bd2 = BigDecimal.valueOf(151);
BigDecimal bd3 = BigDecimal.valueOf(151.12345);
BigDecimal bd4 = BigDecimal.valueOf(1.5);
// 1.001(10.001 ÷ 1.5 の商)スケール:2
System.out.println(bd1.divideToIntegralValue (bd4));
// 1
System.out.println(bd2.divideToIntegralValue (bd4));
// 100.0000(100 → 151 ÷ 1.5 の商)スケール:4
System.out.println(bd3.divideToIntegralValue (bd4));
剰余(remainder メソッド)
remainder メソッドは、引数の BigDecimal で除算を行った際の剰余を示す BigDecimal インスタンスを返します。
remainder メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
他の除算のメソッド同様に、ゼロ除算を行った場合は例外が発生します。また、戻り値のBigDecimal インスタンスのスケールは、メソッドを呼び出す BigDecimal インスタンスおよび引数の BigDecimal インスタンスのスケールのいずれか大きいほう(小数の桁が多いほう)に合わせられます。
BigDecimal bd1 = BigDecimal.valueOf(1100);
BigDecimal bd2 = BigDecimal.valueOf(125);
BigDecimal bd3 = BigDecimal.valueOf(125.05);
// 100(1100 ÷ 125 の剰余)スケール:0
System.out.println(bd1.remainder (bd2));
// 99.60(1100 ÷ 125.05 の商)スケール:2
System.out.println(bd1.remainder (bd3));
商と剰余(divideAndRemainder メソッド)
remainder メソッドは、引数の BigDecimal で除算を行った際の剰余を示す BigDecimal の配列を返します。
divideAndRemainder メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
他の除算のメソッド同様に、ゼロ除算を行った場合は例外が発生します。戻り値の配列の0番目には商の値、1番目には剰余の値が格納され、これらの値の精度およびスケールは divideToIntegralValue メソッドおよび remainder メソッドの戻り値と同じ内容になります。
BigDecimal bd1 = BigDecimal.valueOf(1100);
BigDecimal bd2 = BigDecimal.valueOf(10.99);
BigDecimal bd3 = BigDecimal.valueOf(125.05);
BigDecimal[] result1 = bd1.divideAndRemainder(bd2);
BigDecimal[] result2 = bd1.divideAndRemainder(bd3);
// 商:1E+2 剰余:1
System.out.println(result1[0]);
System.out.println(result1[1]);
// 商:8 剰余:99.60
System.out.println(result2[0]);
System.out.println(result2[1]);
数値の比較
大小比較(compareTo メソッド)
compareTo メソッドは、引数の BigDecimal インスタンスの数値の大小を比較し、結果を int値(0, 1, -1 の何れか)で返します。
compareTo メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
BigDecimal クラスは不等号の演算子での比較はできないため、このメソッドを利用して数値の大小を比較する必要があります。メソッドを呼び出したインスタンスの値の方が大きい場合は 1 、小さい場合は -1 、値が同じ場合は 0 を返します。
BigDecimal bd1 = BigDecimal.valueOf(100);
BigDecimal bd2 = BigDecimal.valueOf(50);
BigDecimal bd3 = BigDecimal.valueOf(50);
// 1(メソッド呼び出し元の方が大きい)
System.out.println(bd1.compareTo(bd2));
// -1(メソッド呼び出し元のほうが小さい)
System.out.println(bd3.compareTo(bd1));
// 0(メソッド呼び出し元と引数が同じ数値)
System.out.println(bd2.compareTo(bd3));
大きい値(max メソッド)
min メソッドは、引数の BigDecimal と値を比較し、値が大きいほうの BigDecimal インスタンスを返します。値が同じだった場合は、『メソッドを呼び出したインスタンス』自身を返します。
max メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
BigDecimal bd1 = BigDecimal.valueOf(100);
BigDecimal bd2 = BigDecimal.valueOf(99);
BigDecimal bd3 = BigDecimal.valueOf(99);
/* 値が大きいほうのインスタンスが返されることを確認する */
// true: bd1.max(bd2)) の戻り値は bd1
System.out.println(bd1 == bd1.max(bd2));
// true: bd2.max(bd1)) の戻り値は bd1
System.out.println(bd1 == bd3.max(bd1));
/* 値が同じ場合はメソッドの呼び出し元が返されることを確認する */
// true: bd2.max(bd3)) の戻り値は bd2
System.out.println(bd2 == bd2.max(bd3));
// true: bd3.max(bd2)) の戻り値は bd3
System.out.println(bd3 == bd3.max(bd2));
小さい値(min メソッド)
min メソッドは、引数の2つの BigDecimal の値のうち、値が小さいほうの BigDecimal インスタンス』を返します。値が同じだった場合は、『メソッドを呼び出したインスタンス』自身を返します。
min メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
BigDecimal bd1 = BigDecimal.valueOf(100);
BigDecimal bd2 = BigDecimal.valueOf(99);
BigDecimal bd3 = BigDecimal.valueOf(99);
/* 値が小さいほうのインスタンスが返されることを確認する */
// true: bd1.min(bd2)) の戻り値は bd2
System.out.println(bd2 == bd1.max(bd2));
// true: bd2.min(bd1)) の戻り値は bd2
System.out.println(bd2 == bd3.max(bd1));
/* 値が同じ場合はメソッドの呼び出し元が返されることを確認する */
// true: bd2.min(bd3)) の戻り値は bd2
System.out.println(bd2 == bd2.max(bd3));
// true: bd3.min(bd2)) の戻り値は bd3
System.out.println(bd3 == bd3.max(bd2));
絶対値・符号
符号の逆転(negate メソッド)
nagate メソッドは BigDecimal の符号を反転した BigDecimal インスタンスを返します。
negate メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
MathContext を指定しない場合は引数の精度およびスケールで、MathContext を指定した場合は指定した精度で符号を反転した値を返します。
BigDecimal bd1 = BigDecimal.valueOf(12345);
// -12345 (符号を反転した値)
System.out.println(bd1.negate());
// -1.23E+4(精度を3として符号を反転した値)
System.out.println(bd1.negate(new MathContext(3)));
plus メソッド
plus メソッドは、negate メソッドの反対(符号を反転せずに返す)処理として準備されています。
plus メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
plus メソッドでは、MathContext を指定しない場合は呼び出し元のインスタンスをそのまま返し、MathContext を指定した場合は呼び出し元の値を指定した精度で返します。精度を変更したい場合にのみ利用する機会があるメソッドと言えるでしょう。
BigDecimal bd1 = BigDecimal.valueOf(12345);
// 12345 (メソッド呼び出し元をそのまま返却)
System.out.println(bd1.plus());
// 1.23E+4(精度を3とした値)
System.out.println(bd1.plus(new MathContext(3)));
絶対値(abs メソッド)
abs メソッドでは、BigDecimal の絶対値を表す BigDecimal インスタンスを返します。
abs メソッド - Java 21 APIドキュメント(java.math.BigDecimal)
メソッド呼び出し元の値が 0 の場合:
呼び出し元のインスタンスを返します。
正の値の場合:
精度を指定しない、または引数の MathContext で指定した精度が 0 または呼び出し元の精度より大きい場合は、メソッドを呼び出したインスタンスをそのまま返却します。それ以外の場合は、MathContext を引き渡して plus メソッドを実行した結果を返却します。
負の値の場合:
符号を反転した値を持つBigDecimal インスタンスを返却します。内部的には、negate メソッドを実行した結果(引数に MathContext が指定されていた場合は引き渡して実行した結果)が返されます。
この結果、返却値の精度は、引数の MathContext で指定した精度が呼び出し元の精度より小さい場合はその精度、それ以外の場合はメソッドを呼び出したインスタンスの精度となります。
型の変換
プリミティブ型からの変換
プリミティブ型の変数値から BigDecimal クラスへの変換については、前述のとおり整数値、小数値それぞれに対して valueOf メソッドが準備されています。
ただし、valueOf メソッドでは精度や小数値のスケールを設定するオーバーロードメソッドは準備されていないため、これらを同時に設定したい場合は new キーワードを用いてインスタンスを生成する必要があります。
プリミティブ型への変換
BigDecimal の値をプリミティブ型の値に変換するメソッドは、以下の一覧のメソッドが準備されています。BigDecimal の数値が型の範囲を超える場合があるため、整数型への変換メソッドでは正確に変換できたかを検知できるメソッドも準備されています。
メソッド名 | 変換先の型 | 説明 |
intValue | int | BigDecimal の値を int 型に変換する。 オーバーフローは検知しない。 |
intValueExact | int | BigDecimal の値を int 型に変換する。 オーバーフロー時はArithmeticExceptionをスローする。 |
longValue | long | BigDecimal の値をlong 型に変換する。 オーバーフローは検知しない。 |
longValueExact | long | BigDecimal の値を long 型に変換する。 オーバーフロー時はArithmeticExceptionをスローする。 |
floatValue | float | BigDecimal の値を float 型に変換する。 |
doubleValue | double | BigDecimal の値を double 型に変換する。 |
以下は int の最大値 + 1 の値を、intValue メソッドおよび intValueExact メソッドで変換するサンプルコードです。intValue メソッドで変換した場合、オーバーフローのため値が int の最小値として変換されていることがわかります。
BigDecimal bdIntMax = BigDecimal.valueOf(Integer.MAX_VALUE);
BigDecimal bdPlusOne = bdIntMax.add(BigDecimal.ONE);
// 「intの最大値」のBigDecimalをint値に変換
int imtMax = bdIntMax.intValue();
// imtMax:2147483647
System.out.println("imtMax:" + imtMax);
// 「intの最大値 + 1」のBigDecimalをint値に変換
int intOverflow = bdPlusOne.intValue();
// intOverflow:-2147483648
System.out.println("intOverflow:" + intOverflow);
// intの範囲を超えているためArithmeticException(Overflow)が発生
int intExact = bdPlusOne.intValueExact();
小数値の場合はオーバーフローを検知せず、表現可能な範囲を超えた場合には正または負の無限大(値が大きすぎる場合)、もしくは正または負のゼロ(値が小さすぎる場合)として変換されます。
BigDecimal bdFloatMax = BigDecimal.valueOf(Float.MAX_VALUE);
BigDecimal bdMultiTen = bdFloatMax.multiply(BigDecimal.TEN);
// floatの最大値
float floatMax = bdFloatMax.floatValue();
// floatMax:3.4028235E38
System.out.println("floatMax:" + floatMax);
// 「floatの最大値×10」のBigDecimalをfloat値に変換
float floatOverflow = bdMultiTen.floatValue();
// floatOverflow:Infinity
System.out.println("floatOverflow:" + floatOverflow);
次回以降の記事では、BigDecimal で階乗や平方根を扱うなど、今回ご紹介した以外のメソッドの利用方法や、精度、スケールと切り上げ、切り捨てなどを指定する内容の詳細についてご紹介します。