0Pricing
Java Academy · レッスン

金額計算のための BigDecimal

金額や税金の計算で浮動小数点の精度誤差を避けるために BigDecimal を使用します。

「金額計算のための BigDecimal」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

金融計算のためのBigDecimal

浮動小数点型(double、float)は、2進数表現が原因で、すべての10進数値を正確に表現できるわけではありません。金額や税金にはBigDecimalを使用します。

浮動小数点の問題

2進浮動小数点では、ごく小さな丸め誤差が発生します。金融アプリケーションでは、こうした誤差が蓄積して実際のバグにつながります。

double price = 0.1 + 0.2;
System.out.println(price);          // 0.30000000000000004 (WRONG!)
System.out.println(price == 0.3);   // false

// Real bug: total never equals expected
double total = 0;
for (int i = 0; i < 10; i++) total += 0.1;
System.out.println(total); // 0.9999999999999999

BigDecimalを安全に作成する

BigDecimalは、doubleからではなく、必ずStringから作成します。doubleコンストラクターを使用すると、浮動小数点の不正確さを引き継いでしまいます。

import java.math.BigDecimal;

// WRONG: double constructor
BigDecimal wrong = new BigDecimal(0.1);
System.out.println(wrong); // 0.1000000000000000055511...

// CORRECT: String constructor
BigDecimal correct = new BigDecimal("0.1");
System.out.println(correct); // 0.1

// Or use valueOf (also safe)
BigDecimal safe = BigDecimal.valueOf(0.1);
System.out.println(safe); // 0.1

BigDecimalによる基本的な算術演算

BigDecimalでは、算術演算にメソッド呼び出しを使用します。利用できるメソッドはadd()、subtract()、multiply()、divide()です。これらは新しいBigDecimalインスタンスを返します(不変です)。

import java.math.BigDecimal;

BigDecimal price = new BigDecimal("19.99");
BigDecimal tax   = new BigDecimal("0.08");
BigDecimal qty   = new BigDecimal("3");

BigDecimal subtotal = price.multiply(qty);
BigDecimal taxAmount = subtotal.multiply(tax);
BigDecimal total = subtotal.add(taxAmount);

System.out.println(subtotal);  // 59.97
System.out.println(taxAmount); // 4.7976
System.out.println(total);     // 64.7676

丸めモード

RoundingModeは、BigDecimalの丸め方を制御します。主なモードは次のとおりです。

  • HALF_UP — 標準的な丸め(0.5を切り上げます)
  • HALF_EVEN — 偶数丸め(0.5を最も近い偶数に丸めます)
  • DOWN — ゼロ方向に切り捨てます
  • CEILING — 正の無限大方向に丸めます
import java.math.*;

BigDecimal val = new BigDecimal("2.565");

System.out.println(val.setScale(2, RoundingMode.HALF_UP));   // 2.57
System.out.println(val.setScale(2, RoundingMode.HALF_EVEN)); // 2.56 (banker's)
System.out.println(val.setScale(2, RoundingMode.DOWN));      // 2.56
System.out.println(val.setScale(2, RoundingMode.CEILING));   // 2.57

除算とスケール

BigDecimalで除算する場合は、循環しない小数でない限りArithmeticExceptionが発生するのを避けるため、スケールと丸めモードを指定する必要があります。

import java.math.*;

BigDecimal revenue = new BigDecimal("1000.00");
BigDecimal months  = new BigDecimal("3");

// Without scale → ArithmeticException (non-terminating decimal)
try {
    revenue.divide(months);
} catch (ArithmeticException e) {
    System.out.println("Need to specify scale!");
}

// Correct: specify scale and rounding
BigDecimal monthly = revenue.divide(months, 2, RoundingMode.HALF_UP);
System.out.println(monthly); // 333.33

BigDecimalの比較

値を比較するにはequals()ではなく、compareTo()を使用します。equals()はスケールも考慮するため、2.0と2.00はequals()では等しくありませんが、compareTo()では等しくなります。

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");

System.out.println(a.equals(b));        // false (different scale)
System.out.println(a.compareTo(b));     // 0 (same value)
System.out.println(a.compareTo(b) == 0); // true

// For sorting in collections
import java.util.*;
List<BigDecimal> prices = Arrays.asList(
    new BigDecimal("3.50"), new BigDecimal("1.00"), new BigDecimal("2.75")
);
Collections.sort(prices);
System.out.println(prices); // [1.00, 2.75, 3.50]

有効桁数のためのMathContext

MathContextは、精度(有効桁数)と丸めモードを指定します。小数点以下の桁数ではなく、一定の有効桁数の精度が必要な場合に使用します。

import java.math.*;

MathContext mc = new MathContext(4, RoundingMode.HALF_UP);

BigDecimal a = new BigDecimal("1234567");
BigDecimal b = new BigDecimal("3");

BigDecimal result = a.divide(b, mc);
System.out.println(result); // 4.115E+5 (4 significant digits)

// DECIMAL32, DECIMAL64, DECIMAL128 are standard MathContexts
BigDecimal precise = new BigDecimal("355").divide(
    new BigDecimal("113"), MathContext.DECIMAL64);
System.out.println(precise); // 3.141592920353982...

ショッピングカートの例

明細項目、税金、割引、最終合計を含む、BigDecimalを使用した完全なショッピングカート計算です。

import java.math.*;

record LineItem(String name, BigDecimal price, int qty) {
    BigDecimal subtotal() {
        return price.multiply(new BigDecimal(qty));
    }
}

var items = List.of(
    new LineItem("Laptop",  new BigDecimal("999.00"), 1),
    new LineItem("Case",    new BigDecimal("29.99"),  2),
    new LineItem("Charger", new BigDecimal("49.99"),  1)
);

BigDecimal subtotal = items.stream()
    .map(LineItem::subtotal)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

BigDecimal taxRate = new BigDecimal("0.09");
BigDecimal tax = subtotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
BigDecimal total = subtotal.add(tax);

System.out.println("Subtotal: " + subtotal);
System.out.println("Tax:      " + tax);
System.out.println("Total:    " + total);

Stripe:セント単位の通貨

Stripeなどの決済処理サービスでは、10進数の問題を完全に避けるため、金額を整数のセント単位で保存します。セント単位と表示用の金額を相互に変換するのは、一般的なパターンです。

import java.math.*;

// Store as cents (long) for database / API
long amountCents = 999_99L; // $999.99

// Convert to BigDecimal for display
BigDecimal amount = BigDecimal
    .valueOf(amountCents)
    .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);

System.out.println("Amount: $" + amount); // Amount: $999.99

// Convert user input back to cents
BigDecimal input = new BigDecimal("49.95");
long cents = input.multiply(new BigDecimal("100"))
    .setScale(0, RoundingMode.HALF_UP).longValueExact();
System.out.println("Cents: " + cents); // Cents: 4995

BigDecimalとdoubleの使い分け

用途に合った型を選択します。

  • BigDecimal:正確な10進数表現が重要な金額、税金、請求書など
  • double:速度が重要で、小さな誤差が許容される科学計算、グラフィックス、機械学習など
  • int/long cents:決済APIや、データベースでの金額の保存

クイックチェック

スケールに関係なく、2つのBigDecimal値が数値的に等しいかを確認するには、どのBigDecimal比較メソッドを使用すべきですか。

まとめ:金融計算のためのBigDecimal

重要なポイント:

  • double/floatでは丸め誤差が発生するため、金額には決して使用しません
  • BigDecimalはdoubleからではなく、StringまたはBigDecimal.valueOf()から作成します
  • 算術演算にはadd()、subtract()、multiply()、divide()を使用します
  • 除算時は必ずスケールとRoundingModeを指定します
  • 等値判定にはequals()ではなくcompareTo()を使用します
  • データベースやAPIで扱う場合は、金額を整数のセント単位で保存することを検討します

よくある質問

「金額計算のための BigDecimal」レッスンは無料ですか?

はい。「金額計算のための BigDecimal」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「金額計算のための BigDecimal」で何を学びますか?

金額や税金の計算で浮動小数点の精度誤差を避けるために BigDecimal を使用します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「金額計算のための BigDecimal」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Math クラスの基本
  2. 整数演算とオーバーフロー
  3. 金額計算のための BigDecimal
  4. NumberFormat と printf
← Java Academyに戻る