本ページはプロモーションが含まれています

Java入門

【Java】BigDecimalの初期化・使い方|計算・比較・丸めを解説

BigDecimal(計算・比較・丸め)

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

Javaでdouble型を使って計算すると0.1 + 0.2 = 0.30000000000000004のような誤差が発生します。

BigDecimalはこの問題を解決し、金額計算や税率計算を正確に行うためのクラスです。

実際に私も決済システムの開発でdouble型の誤差に遭遇し、BigDecimalに切り替えて問題を解決した経験があります。

この記事では、BigDecimalの初期化・四則演算(add/subtract/multiply/divide)を解説します。

あわせて比較(compareTo)や丸め処理(setScale/RoundingMode)も、サンプルコード付きで紹介します。

この記事でわかること

  • BigDecimalの基本的な役割と必要性を理解できる
  • double型との違いを明確に説明できるようになる
  • BigDecimalの正しい生成方法、演算、比較方法を習得できる
  • 初心者が陥りがちな4つの落とし穴とその対策を学べる
  • 通貨計算などの実践的なテクニックを身につけられる

BigDecimalとは?doubleとの違いと必要な理由

BigDecimalの基本とdouble型との違いを示す図解

まずはBigDecimalがどのようなもので、なぜ必要なのかを見ていきましょう。

特にdouble型との比較を通して、BigDecimalの存在価値を明らかにします。

BigDecimalとは何か?概要とできること

BigDecimalは、Javaで正確な10進数の計算を行うために用意されたクラスです。

java.mathパッケージに属しており、小数点以下の数値を誤差なく表現できます。

私たちが普段使う数字は0から9の10個の数字を組み合わせる10進数です。

一方、コンピュータの内部では0と1の2つの数字で表現する2進数が使われています。

doubleやfloatといった型は、この2進数で小数を扱おうとします。

0.1のような単純な小数でさえ、内部的には無限に続く循環小数になり、誤差が生まれてしまいます。

BigDecimalは、double型のこの丸め誤差を解決するために作られました。

内部的に数値を10進数のまま保持するため、私たちが期待する通りの正確な計算が可能です。

特に、お金の計算や精密な科学技術計算など、わずかな誤差も許されない場面でBigDecimalの真価を発揮します。

なぜBigDecimalが必要なのか? doubleとの比較

BigDecimalの必要性を理解するために、double型で計算した場合の問題点を見てみましょう。

例えば、0.1 + 0.2という簡単な足し算を考えます。答えはもちろん0.3になるはずです。

しかし、この計算をdouble型で行うと、驚くべき結果が返ってきます。

text
System.out.println(0.1 + 0.2)

実行結果:

text
0.30000000000000004

ごくわずかながら誤差が生じてしまいます。この誤差は、doubleが2進数で小数を近似的に表現していることに起因します。

一度の計算では小さな誤差でも、計算を繰り返すうちに無視できない大きなずれになる可能性があります。

一方で、同じ計算をBigDecimalで行うとどうなるでしょうか。

java
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.2");
        System.out.println(a.add(b));
    }
}

実行結果:

text
0.3

期待通り、正確な0.3という結果が得られました。計算の正確性を担保したい場合にBigDecimalは必須です。

BigDecimalを使うメリット・デメリット

BigDecimalの利用には、メリットとデメリットの両方があります。

メリット:

  • 高い精度: 小数計算で誤差が発生しません。金融システムなど、正確性が最優先される場面で必須です。
  • 柔軟な丸め処理: 四捨五入、切り捨て、切り上げなど、8種類の丸めモードを細かく指定できます。消費税の計算などで便利です。

デメリット:

  • パフォーマンス: doubleやlongなどのプリミティブ型と比べて、オブジェクトを生成するため処理速度が遅く、メモリも多く消費します。
  • 記述の煩雑さ: 四則演算が+や-のような演算子ではなく、add()やsubtract()といったメソッド呼び出しになるため、コードが長くなりがちです。

すべての数値計算をBigDecimalで行う必要はありません。

パフォーマンスが重視され、多少の誤差が許容されるグラフィック処理などではdoubleが適しています。用途に応じた使い分けが重要です。

あわせて読む

BigDecimalの使い方:初期化・四則演算・比較

BigDecimalの初期化・四則演算・比較方法を示す図解

ここでは、BigDecimalを実際に使うための基本的な操作方法を解説します。

オブジェクトの作り方から、四則演算、そして値の比べ方まで、サンプルコードを交えて見ていきましょう。

BigDecimalの初期化(生成)方法

BigDecimalのオブジェクトを生成するには、いくつかの方法がありますが、文字列を引数に取るコンストラクタを使うのが最も安全で確実です。

java
// 推奨される方法: 文字列から生成
BigDecimal bd1 = new BigDecimal("10.5");

// doubleから生成(非推奨)
BigDecimal bd2 = new BigDecimal(0.1);

System.out.println("文字列から: " + bd1);
System.out.println("doubleから: " + bd2);

実行結果:

text
文字列から: 10.5
doubleから: 0.1000000000000000055511151231257827021181583404541015625

doubleを引数にすると、doubleが元々持っている誤差までBigDecimalオブジェクトに変換されてしまいます。

誤差を引き継いでしまっては、BigDecimalを使う意味がありません。必ず文字列コンストラクタを使いましょう。

また、よく使う0や1、10については、定数が用意されています。

java
BigDecimal zero = BigDecimal.ZERO; // 0
BigDecimal one = BigDecimal.ONE;   // 1
BigDecimal ten = BigDecimal.TEN;   // 10

valueOf()メソッドを使う方法もあります。

valueOf(double val)は内部でDouble.toString(val)を呼び出すため、文字列コンストラクタと同じ結果になり安全です。

java
BigDecimal bd3 = BigDecimal.valueOf(10.5); // こちらも安全
System.out.println(bd3); // 10.5

BigDecimalの四則演算:add, subtract, multiply, divide

BigDecimalの四則演算は、それぞれ専用のメソッドで行います。

  • 加算: add()
  • 減算: subtract()
  • 乗算: multiply()
  • 除算: divide()

add()やsubtract()などのメソッドは、計算結果を新しいBigDecimalオブジェクトとして返します(詳しくは後述の「不変性」で解説します)。

java
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");

// 加算: 10 + 3
BigDecimal resultAdd = a.add(b);
System.out.println("加算: " + resultAdd); // 13

// 減算: 10 - 3
BigDecimal resultSubtract = a.subtract(b);
System.out.println("減算: " + resultSubtract); // 7

// 乗算: 10 * 3
BigDecimal resultMultiply = a.multiply(b);
System.out.println("乗算: " + resultMultiply); // 30

// 除算: 10 / 3
// このままだと割り切れないため例外が発生する
// BigDecimal resultDivide = a.divide(b); 

// 正しくは、桁数と丸めモードを指定する
BigDecimal resultDivide = a.divide(b, 5, RoundingMode.HALF_UP);
System.out.println("除算: " + resultDivide); // 3.33333

除算については、割り切れない場合にArithmeticExceptionという例外が発生する可能性があるため注意が必要です。

この除算時の例外については後ほど詳しく解説します。

BigDecimalの剰余(余り)計算

割り算の余りを求めるにはremainder()メソッドを使います。また、商と余りを同時に取得できるdivideAndRemainder()も便利です。

java
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("10");
        BigDecimal b = new BigDecimal("3");

        // 余りを取得
        BigDecimal remainder = a.remainder(b);
        System.out.println("余り: " + remainder); // 1

        // 商と余りを同時に取得
        BigDecimal[] results = a.divideAndRemainder(b);
        System.out.println("商: " + results[0]);  // 3
        System.out.println("余り: " + results[1]); // 1
    }
}

divideAndRemainder()はBigDecimalの配列を返し、[0]が商、[1]が余りです。

割り勘計算や在庫の均等配分など、商と余り両方が必要な場面で重宝します。

BigDecimalの比較:equalsとcompareToの違い

BigDecimalの値を比較するにはequals()とcompareTo()の2つのメソッドがありますが、この2つは役割が異なります。

  • equals(): 数値とスケール(小数点以下の桁数)の両方が完全に一致する場合にtrueを返します。
  • compareTo(): 数値の大小を比較します。スケールは無視されます。
    • a.compareTo(b)の結果
      • -1: aがbより小さい
      • 0: aとbが等しい
      • 1: aがbより大きい

具体的なコードで違いを見てみましょう。

java
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");

// equals()での比較
System.out.println("equals: " + bd1.equals(bd2)); // false

// compareTo()での比較
System.out.println("compareTo: " + (bd1.compareTo(bd2) == 0)); // true

1.0と1.00は、数値的には同じですが、スケール(小数点以下の桁数)がそれぞれ1と2で異なります。そのため、equals()はfalseを返します。

金額の比較など、純粋に数値として等しいかを確認したい場合はcompareTo()を使いましょう。

equals()は、スケールまで厳密に一致しているかを確認したい特殊なケースで利用します。

BigDecimalでハマりやすい落とし穴と対策

BigDecimalの使用でハマりやすい落とし穴と対策を示す図解

BigDecimalは便利なクラスですが、使い方を誤ると予期せぬエラーやバグの原因になります。私自身も実務で苦い経験があります。

divideの丸めモードを指定し忘れ、ArithmeticExceptionを本番環境で発生させてしまい、緊急対応に追われました。

スケールと精度:丸め処理の重要性

BigDecimalでは、スケール(scale)と精度(precision)を理解することが重要です。

スケールは小数点以下の桁数、精度は数値全体の有効桁数を表します。例えば123.45の場合、スケールは2、精度は5です。

特に、計算結果の桁数を調整するsetScale()メソッドを使う際には注意が必要です。

例えば、1.235という数値を小数点以下2桁に丸めたいとします。

java
import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {
    public static void main(String[] args) {
        BigDecimal num = new BigDecimal("1.235");

        // 小数点以下2桁に、四捨五入で丸める
        BigDecimal rounded = num.setScale(2, RoundingMode.HALF_UP);
        System.out.println(rounded); // 1.24

        // 丸めモードを指定しないと例外が発生する可能性がある
        try {
            BigDecimal error = new BigDecimal("1.235").setScale(2);
        } catch (ArithmeticException e) {
            System.err.println("エラー: " + e.getMessage());
        }
    }
}

実行結果:

text
1.24
エラー: Rounding necessary

setScale()で桁数を減らす場合、1.235を1.23にするには端数の5をどう処理するか決めなければなりません。

この「どう処理するか」を指定するのが丸めモード(RoundingMode)です。

丸めモードを指定せずにsetScale()を呼ぶと、「丸めが必要なのにモードが指定されていない」という理由でArithmeticExceptionが発生します。

計算の最終結果を特定の桁数に揃える場合は、必ずsetScale()とRoundingModeをセットで使いましょう。

あわせて読む

除算時のArithmeticException:例外処理

BigDecimalの落とし穴として最も有名なのが、除算時のArithmeticExceptionです。

これは、割り算の結果が無限小数になる(割り切れない)場合に発生します。

実際に10 ÷ 3を計算すると3.333...となり、割り切れません。

java
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");

// 割り切れないため ArithmeticException が発生する
// a.divide(b); 

この例外を回避するには、divide()メソッドのオーバーロードを使い、スケール(小数点以下の桁数)と丸めモードを明示的に指定します。

java
import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("10");
        BigDecimal b = new BigDecimal("3");

        // 小数点以下5桁まで求め、それ以降は四捨五入する
        BigDecimal result = a.divide(b, 5, RoundingMode.HALF_UP);
        System.out.println(result); // 3.33333
    }
}

スケールと丸めモードを指定することで、計算が指定桁数で打ち切られ、例外の発生を防げます。

特にユーザーの入力値など、どのような数値が来るか分からない計算では、必ず桁数と丸めモードを指定する習慣をつけましょう。

BigDecimalの不変性:注意点とコピー

BigDecimalオブジェクトは不変(Immutable)です。

これは、一度作成したオブジェクトの内部状態(値)が決して変わらないという性質を意味します。

add()やsetScale()などのメソッドを呼び出しても、元のオブジェクトの値は変化しません。

こうした演算メソッドは、計算結果を持つ新しいBigDecimalオブジェクトを生成して返すのです。

初心者がやりがちな間違いが、この戻り値を受け取らないことです。

java
// 間違った例
BigDecimal price = new BigDecimal("1000");
BigDecimal taxRate = new BigDecimal("0.1");

// price.add(price.multiply(taxRate)); // 計算結果を捨ててしまっている
// System.out.println(price); // 1000 のまま

// 正しい例
BigDecimal tax = price.multiply(taxRate);
BigDecimal totalPrice = price.add(tax); // 戻り値を受け取る

System.out.println(totalPrice); // 1100.0

price.add(tax)を実行しても、price自体の値は1000のままです。

計算結果である1100は、新しく作られたBigDecimalオブジェクトとして返されるので、それをtotalPriceという別の変数で受け取る必要があります。

この不変性という性質は、複数の場所から同じオブジェクトを参照しても値が書き換わる心配がないため、安全なプログラミングにつながります。

BigDecimalの演算を行う際は、必ず戻り値を変数に代入することを忘れないでください。

BigDecimalのnull判定と安全な初期化

BigDecimalはオブジェクト型のため、nullが代入される可能性があります。

nullのまま演算メソッドを呼ぶとNullPointerExceptionが発生するため、外部入力やデータベースから取得した値を扱う際は注意が必要です。

java
import java.math.BigDecimal;
import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        // nullチェックしてからデフォルト値を設定するパターン
        BigDecimal input = null;
        BigDecimal safeValue = (input != null) ? input : BigDecimal.ZERO;
        System.out.println(safeValue); // 0

        // Optionalを使ったパターン
        BigDecimal result = Optional.ofNullable(input)
                                    .orElse(BigDecimal.ZERO);
        System.out.println(result); // 0
    }
}

データベースのNULLABLEカラムや、ユーザー入力の未入力フィールドなど、nullが入り得る場面では、計算前に必ずnullチェックを行いましょう。

BigDecimalの実践:応用テクニック

BigDecimalを使いこなす実践的な応用テクニックを示す図解

基本を押さえたところで、次はより実践的なBigDecimalの使い方を見ていきましょう。

MathContextによる精度管理、丸めモードの使い分け、通貨・パーセントの表示形式など、実務で役立つテクニックを紹介します。

MathContext:精度を一括管理する

MathContextは、計算時の精度(有効桁数)と丸めモードをまとめて指定できるクラスです。複数の計算で同じ精度設定を使い回したい場合に便利です。

java
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

public class Main {
    public static void main(String[] args) {
        // 有効桁数5桁、四捨五入のMathContextを作成
        MathContext mc = new MathContext(5, RoundingMode.HALF_UP);

        BigDecimal a = new BigDecimal("123.456789");
        BigDecimal b = new BigDecimal("9.87654321");

        // MathContextを適用した計算
        BigDecimal result = a.multiply(b, mc);
        System.out.println(result); // 1219.3
    }
}

あらかじめ定義された定数もあります。

MathContext.DECIMAL32(7桁)・MathContext.DECIMAL64(16桁)・MathContext.DECIMAL128(34桁)の3つです。用途に応じて選択できます。

RoundingMode:様々な丸めモードを使いこなす

BigDecimalの強力な機能の一つが、豊富な丸めモードです。

java.math.RoundingModeというenumで定義されており、全部で8種類あります。

なお、古いコードで見かけるBigDecimal.ROUND_HALF_UPなどの整数定数はJava 9で非推奨になりました。

新規コードでは必ずRoundingModeを使いましょう。ここでは、特によく使われるものを紹介します。

RoundingMode説明例 (5.5)例 (2.5)例 (-2.5)
HALF_UPいわゆる四捨五入。端数が0.5以上なら切り上げる。63-3
DOWN切り捨て。0から遠ざからないように丸める。52-2
UP切り上げ。0から遠ざかるように丸める。63-3
CEILING正の無限大方向に丸める(天井)。63-2
FLOOR負の無限大方向に丸める(床)。52-3

別の例として、日本の消費税計算(2026年8月時点で標準税率10%)では、端数は「切り捨て」が一般的です(なお軽減税率対象の飲食料品は、2027年4月から2年間8%から1%への引き下げが2026年8月5日に閣議決定されています)。

このような場合にRoundingMode.DOWNが役立ちます。

java
import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {
    public static void main(String[] args) {
        BigDecimal price = new BigDecimal("198");
        BigDecimal taxRate = new BigDecimal("0.1");

        // 消費税を計算し、小数点以下を切り捨てる
        BigDecimal tax = price.multiply(taxRate).setScale(0, RoundingMode.DOWN);
        System.out.println("消費税: " + tax); // 19

        BigDecimal totalPrice = price.add(tax);
        System.out.println("税込価格: " + totalPrice); // 217
    }
}

私が関わったECサイトの開発では、商品ごとの税込価格にRoundingMode.DOWN(切り捨て)を使っていました。

一方、合計金額への課税にはRoundingMode.HALF_UP(四捨五入)を使うなど、同じシステム内でも丸めモードを使い分けていました。

仕様書に「端数処理」の記載がない場合は、必ず確認を取ることをお勧めします。

業務要件に応じて適切な丸めモードを選択することが、正確なシステムを構築する上で重要です。

NumberFormat:通貨やパーセント表示

BigDecimalで計算した結果を、ユーザーに分かりやすく表示する際にはNumberFormatクラスが便利です。

例えば、数値を「¥1,234」のような通貨形式や、「50%」のようなパーセント形式に整形できます。

java
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        BigDecimal amount = new BigDecimal("12345.67");

        // 日本の通貨形式にフォーマット
        NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(Locale.JAPAN);
        System.out.println(currencyFormatter.format(amount)); // ¥12,346(JDK 21・23では全角の円記号で表示される)

        BigDecimal ratio = new BigDecimal("0.55");

        // パーセント形式にフォーマット
        NumberFormat percentFormatter = NumberFormat.getPercentInstance();
        System.out.println(percentFormatter.format(ratio)); // 55%
    }
}

NumberFormatは内部で自動的に丸め処理を行いますが、既定の丸めモードは四捨五入(HALF_UP)ではなく、偶数丸め(HALF_EVEN)です。たとえば12346.5を通貨形式にすると12,346になります。

getCurrencyInstanceにLocaleを指定することで、様々な国の通貨形式に対応できます。

計算はBigDecimalで正確に行い、表示の整形はNumberFormatに任せる、という役割分担が理想的です。

BigDecimalとコレクション:合計値の計算

Listなどのコレクションに格納された複数のBigDecimalオブジェクトの合計値を計算する場面もよくあります。

これは、ループ処理やJava 8以降で利用可能なStream APIを使うことで簡単に実現できます。

for-eachループを使った方法:

java
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<BigDecimal> prices = Arrays.asList(
            new BigDecimal("198.5"),
            new BigDecimal("2050"),
            new BigDecimal("378.99")
        );

        BigDecimal sum = BigDecimal.ZERO;
        for (BigDecimal price : prices) {
            sum = sum.add(price);
        }
        System.out.println("合計金額 (for-each): " + sum); // 2627.49
    }
}

Stream APIを使った方法:

Stream APIのreduceメソッドを使うと、より簡潔に記述できます。

java
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<BigDecimal> prices = Arrays.asList(
            new BigDecimal("198.5"),
            new BigDecimal("2050"),
            new BigDecimal("378.99")
        );

        BigDecimal streamSum = prices.stream()
                                     .reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println("合計金額 (Stream): " + streamSum); // 2627.49
    }
}

どちらの方法でも正しい結果が得られますが、Stream APIを使うとコードがすっきりとします。

プロジェクトのコーディング規約やJavaのバージョンに合わせて使い分けてください。

BigDecimalの型変換:int・String・doubleへの相互変換

実務ではBigDecimalを他の型に変換したり、逆に他の型からBigDecimalを生成したりする場面が頻繁にあります。主要な変換方法をまとめます。

java
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal bd = new BigDecimal("123.456");

        // BigDecimal → int(小数点以下切り捨て)
        int intValue = bd.intValue();
        System.out.println("intValue: " + intValue); // 123

        // BigDecimal → int(小数点以下があると例外を投げる安全版)
        // bd.intValueExact(); // ArithmeticException

        // BigDecimal → double
        double doubleValue = bd.doubleValue();
        System.out.println("doubleValue: " + doubleValue); // 123.456

        // BigDecimal → String
        String strValue = bd.toPlainString();
        System.out.println("toPlainString: " + strValue); // 123.456

        // String → BigDecimal
        BigDecimal fromStr = new BigDecimal("789.012");
        System.out.println("fromString: " + fromStr); // 789.012

        // int → BigDecimal
        BigDecimal fromInt = BigDecimal.valueOf(42);
        System.out.println("fromInt: " + fromInt); // 42
    }
}

intValue()は小数点以下を単純に切り捨てます。

金額計算などで端数の扱いを明示したい場合は、先にsetScale(0, RoundingMode.HALF_UP)で丸めましょう。

そのあとにintValue()を呼ぶと安全です。

intValueExact()は小数部分が残っている場合にArithmeticExceptionを投げるため、データ検証に便利です。

その他の便利なメソッド

四則演算以外にも、BigDecimalには実務で役立つメソッドが用意されています。

java
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal num = new BigDecimal("-123.4500");

        // 絶対値を取得
        System.out.println(num.abs()); // 123.4500

        // 符号を反転
        System.out.println(num.negate()); // 123.4500

        // 累乗(n乗)
        BigDecimal base = new BigDecimal("2");
        System.out.println(base.pow(10)); // 1024

        // 末尾のゼロを除去
        System.out.println(num.stripTrailingZeros()); // -123.45

        // 指数表記を使わない文字列変換
        BigDecimal large = new BigDecimal("12345678901234567890");
        System.out.println(large.toPlainString()); // 12345678901234567890
    }
}

特にstripTrailingZeros()は、データベースから取得した数値を表示する際に便利です。

toPlainString()は指数表記(例: 1.23E+10)を避けたい場合に使います。

よくある質問

Q1. Javaで正確な小数計算を行うにはどうすればいいですか?

A1. BigDecimalクラスを使います。doubleやfloatは2進数で小数を近似的に表現するため誤差が生じますが、BigDecimalは10進数のまま保持するため誤差なく計算できます。

Q2. BigDecimalの初期化はどう書けばいいですか?

A2. 文字列を引数に取るコンストラクタ(例: new BigDecimal("10.5"))を使うのが最も安全です。doubleを直接渡すと、doubleが持つ誤差までBigDecimalに引き継がれてしまいます。

Q3. BigDecimalの比較にequals()とcompareTo()どちらを使うべきですか?

A3. 数値として等しいかを確認したい場合はcompareTo()を使います。equals()は数値とスケール(小数点以下の桁数)の両方が一致する場合のみtrueを返すため、1.0と1.00はequals()ではfalseになります。

Q4. BigDecimalの除算でArithmeticExceptionが発生するのはなぜですか?

A4. 割り算の結果が無限小数になり割り切れない場合に発生します。divide()メソッドでスケール(小数点以下の桁数)と丸めモード(RoundingMode)を明示的に指定することで回避できます。

Q5. BigDecimalとdoubleはどう使い分ければいいですか?

A5. 金融・会計計算やECサイトの料金計算など正確性が最優先される場面ではBigDecimalを使います。3Dグラフィックスの座標計算など速度が重要でわずかな誤差が許容できる場合はdoubleが適しています。

Q6. BigDecimalの演算結果を反映するにはどうすればいいですか?

A6. add()やsetScale()などのメソッドの戻り値を変数に代入する必要があります。BigDecimalは不変(Immutable)オブジェクトのため、メソッドを呼び出しても元のオブジェクトの値は変化せず、計算結果は新しいオブジェクトとして返されます。

BigDecimalのまとめと使い分けガイド

最後に、これまでの内容をまとめ、BigDecimalをさらに深く理解するためのヒントを提供します。

BigDecimalを使うべきケース、使わない方が良いケース

BigDecimalは万能ではありません。特性を理解し、適切な場面で使うことが重要です。

使うべきケース:

  • 金融・会計システム: お金の計算では1円の誤差も許されません。必須です。
  • ECサイトの料金計算: 税金や送料、割引など、正確な金額の算出が求められます。
  • 精密な科学技術計算: 誤差が結果に大きく影響する物理シミュレーションなどで利用します。
  • 法制度に関わる計算: 法律で計算方法や端数処理が厳密に定められている場合。

使わない方が良いケース(doubleなどを検討するケース):

  • パフォーマンスが最優先される計算: 3Dグラフィックスの座標計算や大量の統計データ処理など、速度が重要で、わずかな誤差は許容できる場合。
  • 単純なカウンターやループ変数: 整数で事足りる場面でBigDecimalを使う必要はありません。intやlongを使いましょう。

「正確性が何よりも重要か?」を一つの判断基準にすると、使い分けがしやすくなります。

BigDecimalに関する参考文献・リンク集

さらに学習を深めたい方向けに、公式のドキュメントを紹介します。一次情報にあたることで、より正確で深い知識を得られます。

メソッドの詳細な仕様や、すべてのRoundingModeの説明などが記載されています。

困ったときには、まず公式ドキュメントを参照する習慣をつけることをお勧めします。

なお、2026年9月時点でもJava 25が最新LTSです(Java 27は2026年9月リリースの非LTS版で、次期LTSのJava 29は2027年9月予定)。

BigDecimal APIはJava 8以降で大きな変更がないため、本記事のコードはJava 8以降のどのバージョンでも動作します。

BigDecimalのパフォーマンスチューニング

BigDecimalはdoubleに比べて低速ですが、使い方を工夫することでパフォーマンスへの影響を最小限に抑えられます。

  • valueOf()を積極的に使う: BigDecimal.valueOf(10)のように、long型の引数を取るvalueOfメソッドは、0から10までのよく使われる値をキャッシュしています。0〜10の範囲内であれば、キャッシュによりnew BigDecimal("10")よりも高速です。
  • ループ内でのオブジェクト生成を避ける: ループの中で何度も同じ値のBigDecimalをnewしている場合、ループの外で一度だけ生成して使い回すことで、不要なオブジェクト生成を減らせます。
  • 不要な精度で計算しない: 計算途中のスケールが不必要に大きくなると、パフォーマンスが低下します。中間結果に対しても適切にsetScale()を適用し、桁数を管理することが有効です。

実務上の目安として、秒間数百回程度のBigDecimal演算であれば、パフォーマンス差はほぼ体感できません。

私の経験では、バッチ処理で数十万件の金額計算を行う場面でも、BigDecimalの演算速度が問題になったことはありません。

ボトルネックはいつもDB I/Oでした。

BigDecimalは、Javaで正確な数値を扱うための強力な武器です。

doubleとの違いを正しく理解し、生成・演算・比較の基本をマスターすれば、もう数値計算の誤差に悩まされることはありません。

特に押さえるべきは3つのポイントです。

new BigDecimal(double)を使わないこと、除算では丸めモードを指定すること、不変性を理解することです。

この3つがバグを未然に防ぎます。

現場での開発では、BigDecimalを直接操作するよりも、通貨計算用のユーティリティクラスを作成してチーム全体で統一する方が安全です。

丸めモードやスケールは、プロジェクト共通の定数として定義しましょう。

個人の判断でバラバラにならないようにするのが、私が10年以上の実務で学んだベストプラクティスです。

正確な金額計算が必要な場面では、今日からBigDecimalを使ってみてください。

REVISIONS / 改訂履歴
REV.DATE内容
42026-10-04記載内容の事実確認と修正
32026-08-302026年8月時点の情報で全体を見直し(古い記述を3件更新)
22026-06-022026年6月時点の情報で全体を見直し
12025-10-18初版
  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

-Java入門