Javaで数値を扱う際、避けては通れないのが「四捨五入」の処理です。

単純に小数点以下を丸めるだけであれば簡単そうに思えますが、Javaには複数の方法が存在し、それぞれ挙動や精度が異なります。

特に金融系のシステムや厳密な計算が求められるアプリケーションでは、適切な丸め処理を選択しないと、累積した誤差が大きな問題を引き起こす可能性があります。

本記事では、Javaにおける四捨五入の代表的な実装手法である Math クラスと BigDecimal クラスを徹底的に比較し、状況に応じた最適な使い分けについて詳しく解説します。

Mathクラスを使用した四捨五入の基本

Javaで最も手軽に四捨五入を行う方法は、java.lang.Math クラスのメソッドを利用することです。

標準ライブラリに含まれているため、インポートの手間もなく、短いコードで記述できるのがメリットです。

Math.roundの仕組みと特徴

Math.round() は、引数として与えられた浮動小数点数 (double または float) を四捨五入し、最も近い整数を返します。

  • Math.round(double a):戻り値は long
  • Math.round(float a):戻り値は int

このメソッドの内部挙動は「引数に0.5を足して Math.floor() を適用する」というロジックに基づいています。

そのため、正の数では一般的な四捨五入になりますが、負の数の扱いには注意が必要です。

任意の桁数で四捨五入する方法

Math.round() は整数値を返すため、小数点第2位や第3位で四捨五入したい場合は、「10のn乗を掛けてから四捨五入し、再び10のn乗で割る」 という手順を踏む必要があります。

Java
public class MathRoundExample {
    public static void main(String[] args) {
        double val = 123.456;

        // 小数点第1位で四捨五入(整数にする)
        long roundedInt = Math.round(val);
        System.out.println("整数に丸め: " + roundedInt);

        // 小数点第3位を四捨五入して、第2位まで表示する場合
        // 1. 100倍する
        // 2. Math.roundで四捨五入する
        // 3. 100.0で割る(double型で割る点に注意)
        double roundedVal = Math.round(val * 100.0) / 100.0;
        System.out.println("小数点第2位まで: " + roundedVal);
    }
}
実行結果
整数に丸め: 123
小数点第2位まで: 123.46

Mathクラスを使う際の注意点

Math.round() は非常に高速ですが、浮動小数点数特有の精度問題を抱えています。

コンピュータ内部では数値を2進数で扱っているため、10進数の 0.1 などを正確に表現できず、微小な誤差(丸め誤差)が発生します。

例えば、非常に大きな数値や、極めて精密な計算が必要な場面で「100倍して戻す」操作を行うと、期待した値から微妙にズレるリスクがあります。

また、負の数に対して -1.5 を四捨五入すると -1 になるなど、一般的な数学の四捨五入とは異なる挙動を示す点にも留意してください。

BigDecimalによる厳密な四捨五入

ビジネスロジックや金融計算において、誤差を許容できない場合に必須となるのが java.math.BigDecimal クラスです。

BigDecimal は、任意の精度で数値を扱うことができ、丸めモードを細かく指定できるのが最大の特徴です。

BigDecimalでの四捨五入の実装手順

BigDecimal で四捨五入を行うには、setScale() メソッドを使用します。

このメソッドには「残したい小数点以下の桁数」と「丸めモード」の2つの引数を渡します。

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

public class BigDecimalExample {
    public static void main(String[] args) {
        // コンストラクタには必ず文字列(String)を渡す
        BigDecimal bd = new BigDecimal("123.456");

        // 小数点第2位まで残し(第3位を四捨五入)、丸めモードにHALF_UPを指定
        BigDecimal rounded = bd.setScale(2, RoundingMode.HALF_UP);

        System.out.println("元の値: " + bd);
        System.out.println("四捨五入後: " + rounded);
    }
}
実行結果
元の値: 123.456
四捨五入後: 123.46

注意:コンストラクタに double を使わない

BigDecimal を生成する際、new BigDecimal(0.1) のように double 型を直接渡してはいけません

これは、渡した時点ですでに double 型の誤差が含まれてしまうためです。

必ず new BigDecimal("0.1") と文字列で指定するか、BigDecimal.valueOf(0.1) を使用するように徹底しましょう。

RoundingMode(丸めモード)の種類と詳細

BigDecimalsetScale() で指定できる RoundingMode には、四捨五入以外にも様々な種類があります。

要件に応じてこれらを使い分けることが、プロフェッショナルな実装への第一歩です。

代表的な丸めモード一覧

定数名説明一般的な呼称
UP0から離れる方向に切り上げる切り上げ
DOWN0に近づく方向に切り捨てる切り捨て
CEILING正の無限大に近い方に丸める数学的な切り上げ
FLOOR負の無限大に近い方に丸める数学的な切り捨て
HALF_UP5以上なら切り上げ、4以下なら切り捨てる一般的な四捨五入
HALF_DOWN5を超えるなら切り上げ、5以下なら切り捨てる五捨六入に近い挙動
HALF_EVEN隣接する数値が等距離の場合、偶数の方へ丸める銀行型丸め

HALF_UP と HALF_EVEN の違い

日本の多くのビジネスシーンで求められるのは HALF_UP です。

一方、HALF_EVEN は「銀行丸め」や「JIS丸め」と呼ばれ、端数がちょうど0.5の場合に、結果が偶数になる方を選択します。

これにより、大量のデータを合算した際の統計的な誤差を最小限に抑えることが可能です。

会計ソフトや統計分析アプリを作成する場合は、どちらのモードが適切かを確認する必要があります。

表示のみを行う場合の四捨五入

計算自体は元の精度で行い、ユーザーに見せる画面や帳票でのみ四捨五入したい場合は、String.format()DecimalFormat クラスを利用するのが便利です。

String.format を使用した書式設定

String.format() を使うと、指定した桁数で値を整形して文字列として取得できます。

デフォルトでは四捨五入(正確には HALF_UP に近い挙動ですが、環境や値により僅かに異なる場合があります)が行われます。

Java
public class FormatExample {
    public static void main(String[] args) {
        double value = 123.4567;
        
        // 小数点第2位まで表示(第3位を四捨五入)
        String formatted = String.format("%.2f", value);
        
        System.out.println("表示用文字列: " + formatted);
    }
}
実行結果
表示用文字列: 123.46

DecimalFormat クラスによる高度な整形

java.text.DecimalFormat を使用すると、3桁区切りのカンマを入れるなどの装飾と同時に四捨五入を行うことができます。

Java
import java.text.DecimalFormat;
import java.math.RoundingMode;

public class DecimalFormatExample {
    public static void main(String[] args) {
        double price = 12345.678;
        
        // パターンを指定(#,###は3桁区切り、.00は小数点第2位まで)
        DecimalFormat df = new DecimalFormat("#,###.00");
        
        // 明示的に丸めモードを指定することも可能
        df.setRoundingMode(RoundingMode.HALF_UP);
        
        System.out.println("整形後の価格: " + df.format(price));
    }
}
実行結果
整形後の価格: 12,345.68

MathクラスとBigDecimal、どちらを選ぶべきか?

ここまで紹介した手法のどちらを使うべきかは、開発しているアプリケーションの性質によって決まります。

判断基準を整理しましょう。

Math.round が適しているケース

パフォーマンスが最優先される処理

ゲームの物理演算や、リアルタイム性が求められる信号処理など、1秒間に何万回も計算を繰り返す場合は、オブジェクトを生成しない Math.round が有利です。

厳密な精度を必要としないUI制御

プログレスバーのパーセンテージ表示や、大まかなグラフのプロットなど、僅かな誤差が問題にならない箇所に適しています。

BigDecimal が適しているケース

金融・会計システム

消費税計算、利息計算、外貨換算など、1円のズレも許されない処理では BigDecimal が必須です。

複雑な丸めルールがある場合

切り捨て・切り上げの混在や、銀行丸めなど、特定の業務仕様に従う必要がある場合です。

非常に大きな(または小さな)数値

double の有効桁数を超える数値を扱う場合に有効です。

実践的なTips:共通ユーティリティの作成

プロジェクト全体で四捨五入のルールを統一するために、以下のようなユーティリティメソッドを用意しておくことが一般的です。

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

public class NumberUtil {
    /**
     指定した桁数で四捨五入を行う共通メソッド
     @param value 対象の数値
     @param scale 残す小数点以下の桁数
     @return 四捨五入後の値
     */
    public static double round(double value, int scale) {
        return BigDecimal.valueOf(value)
                         .setScale(scale, RoundingMode.HALF_UP)
                         .doubleValue();
    }
}

このように BigDecimal.valueOf() をラップしたメソッドを使うことで、呼び出し側は double を使いつつ、内部では精度の高い計算を行うことができます。

まとめ

Javaにおける四捨五入の実装は、「手軽さと速度のMathクラス」か「厳密さと柔軟性のBigDecimalクラス」かという選択に集約されます。

  • 簡単な整数の丸めであれば Math.round() で十分ですが、小数点以下の制御には工夫が必要です。
  • 業務アプリケーションでは、誤差を最小限に抑え、意図した通りの丸めモードを指定できる BigDecimal の使用を強く推奨します。
  • 画面表示が目的であれば String.format()DecimalFormat を活用するのが効率的です。

それぞれの特徴を正しく理解し、浮動小数点数の特性を意識したプログラミングを心がけることで、バグの少ない堅牢なシステムを構築することができます。