概要

売上の前年比、KPI の達成率、原価率の算出など、割合計算は業務システムのあらゆる場面で登場します。double で計算すると丸め誤差が蓄積されるため、帳票や報告書の数値が微妙にずれるリスクがあります。とくに明細行ごとの割合を積み上げて合計を出す処理では、蓄積誤差が最終行で顕在化し、帳票の合計欄が手計算と一致しないという指摘につながることがあります。この記事では、BigDecimal を使った割合の算出、増減率の計算、百分率から実数への逆算を整理します。MathContext でスケールと丸めモードを管理する方法も紹介します。

使いどころ

月次レポートで売上の前月比・前年比を算出する

KPI ダッシュボードで達成率を小数第1位まで表示する

原価率から売価を逆算する

コード例

割合と増減率の計算
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

public class PercentageCalc {

    private static final MathContext MC =
        new MathContext(10, RoundingMode.HALF_UP);

    public static BigDecimal percentage(
            BigDecimal part, BigDecimal total) {
        if (total.compareTo(BigDecimal.ZERO) == 0) {
            throw new ArithmeticException("ゼロ除算");
        }
        return part.divide(total, MC)
            .multiply(new BigDecimal("100"))
            .setScale(1, RoundingMode.HALF_UP);
    }

    public static BigDecimal changeRate(
            BigDecimal previous, BigDecimal current) {
        return current.subtract(previous)
            .divide(previous, MC)
            .multiply(new BigDecimal("100"))
            .setScale(1, RoundingMode.HALF_UP);
    }

    public static void main(String[] args) {
        var total = new BigDecimal("2500");
        var part = new BigDecimal("750");
        System.out.println("割合: " +
            percentage(part, total) + "%");

        var prev = new BigDecimal("1000");
        var curr = new BigDecimal("1250");
        System.out.println("増減率: " +
            changeRate(prev, curr) + "%");
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

var と record で計算結果を構造化できる。コードの意図が型で伝わりやすくなる。

Java 17
// Java 17: var + record で計算結果を構造化
record CalcResult(BigDecimal value, String label) {}
var part = new BigDecimal("750");
var total = new BigDecimal("2500");
var result = new CalcResult(
    percentage(part, total), "売上構成比");
System.out.println(result.label()
    + ": " + result.value() + "%");

Library Comparison

Apache Commons Math統計計算や高度な数値演算が必要な場合。割合計算だけなら標準 API で十分。
Guava(IntMath / DoubleMath)オーバーフローチェック付きの整数演算や、丸めモード指定付きの除算を簡潔に書きたいとき。BigDecimal ベースの割合計算には直接対応しない。整数演算のユーティリティが中心で、業務系の精度管理には BigDecimal を併用する形になる。

注意点

割り算の結果が割り切れない場合は必ず RoundingMode を指定すること。

パーセント表示は「×100」するタイミングに注意。計算途中で百分率にしない。

ゼロ除算は事前チェックで防ぐ。BigDecimal.ZERO との比較には compareTo を使う。

FAQ

割合を表示するときの小数桁数はどうすべきですか?

業務要件次第ですが、小数第1位か第2位が一般的です。setScale で統一します。

増減率がマイナスの場合の表示はどうしますか?

BigDecimal の符号がそのまま正負を表すため、表示時にマイナス記号を付けるだけです。

MathContext と setScale の違いは?

MathContext は有効桁数と丸めモードを管理し、setScale は小数点以下の桁数を指定します。用途に応じて使い分けます。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます