Javaで数値を四捨五入するには、Math.round()が最もシンプルですが、実務ではBigDecimalやRoundingModeを使った精密な制御が必須になります。
この記事では、Math.round()の基本から小数第N位での四捨五入、BigDecimalの丸めモード、浮動小数点の罠、通貨計算の注意点まで、実務で使える知識を網羅的に解説します。
Math.round() の基本(整数への四捨五入)
Math.round() は、浮動小数点数を最も近い整数に四捨五入するメソッドです。Java標準ライブラリに含まれており、import不要で使えます。
| シグネチャ |
引数 |
戻り値 |
Math.round(float a) |
float型 |
int型 |
Math.round(double a) |
double型 |
long型 |
float型の四捨五入
RoundFloat.java
public class RoundFloat {
public static void main(String[] args) {
float a = 3.5f;
float b = 3.4f;
float c = -2.5f;
System.out.println(Math.round(a)); // 4
System.out.println(Math.round(b)); // 3
System.out.println(Math.round(c)); // -2
}
}
double型の四捨五入
RoundDouble.java
public class RoundDouble {
public static void main(String[] args) {
double x = 7.5;
double y = 7.4999;
double z = -3.5;
System.out.println(Math.round(x)); // 8
System.out.println(Math.round(y)); // 7
System.out.println(Math.round(z)); // -3
}
}
Math.round() の動作ルール
- 内部的には
Math.floor(a + 0.5) と同等の処理を行う
- float型 → int型を返す、double型 → long型を返す
- 引数が
NaN の場合は 0 を返す
- 引数が
Integer.MAX_VALUE/Long.MAX_VALUE 以上の場合はそのまま返す
小数第N位での四捨五入(10の累乗を使った手法)
Math.round() は整数への四捨五入しかできませんが、10の累乗を組み合わせることで任意の小数桁で四捨五入できます。
基本パターン
RoundDecimal.java
public class RoundDecimal {
public static void main(String[] args) {
double value = 3.14159;
// 小数第1位で四捨五入(整数に)
double r1 = Math.round(value * 1) / 1.0;
System.out.println("整数: " + r1); // 3.0
// 小数第2位で四捨五入(小数第1位まで)
double r2 = Math.round(value * 10) / 10.0;
System.out.println("小数第1位: " + r2); // 3.1
// 小数第3位で四捨五入(小数第2位まで)
double r3 = Math.round(value * 100) / 100.0;
System.out.println("小数第2位: " + r3); // 3.14
// 小数第4位で四捨五入(小数第3位まで)
double r4 = Math.round(value * 1000) / 1000.0;
System.out.println("小数第3位: " + r4); // 3.142
}
}
実行結果
整数: 3.0
小数第1位: 3.1
小数第2位: 3.14
小数第3位: 3.142
注意:この方法は手軽ですが、浮動小数点の誤差が発生する可能性があります。金額計算など厳密さが求められる場面では、後述の BigDecimal を使いましょう。
汎用メソッドにまとめる
RoundUtil.java
public class RoundUtil {
/**
* 指定した小数桁で四捨五入する
* @param value 対象の値
* @param places 残す小数桁数
*/
public static double round(double value, int places) {
double factor = Math.pow(10, places);
return Math.round(value * factor) / factor;
}
public static void main(String[] args) {
System.out.println(round(3.14159, 2)); // 3.14
System.out.println(round(3.14159, 4)); // 3.1416
System.out.println(round(123.456, 0)); // 123.0
}
}
BigDecimal での精密な四捨五入(推奨)
BigDecimal は任意精度の10進数を扱うクラスで、浮動小数点の誤差が発生しないのが最大のメリットです。実務では BigDecimal の使用が強く推奨されます。
基本的な使い方
BigDecimalRound.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalRound {
public static void main(String[] args) {
// 文字列からBigDecimalを生成(推奨)
BigDecimal bd = new BigDecimal("3.14159");
// 小数第2位で四捨五入(HALF_UP = 一般的な四捨五入)
BigDecimal rounded = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(rounded); // 3.14
// 小数第4位で四捨五入
BigDecimal rounded4 = bd.setScale(4, RoundingMode.HALF_UP);
System.out.println(rounded4); // 3.1416
// 整数に四捨五入
BigDecimal rounded0 = bd.setScale(0, RoundingMode.HALF_UP);
System.out.println(rounded0); // 3
}
}
注意:BigDecimalを生成するときは、new BigDecimal(0.1)(doubleコンストラクタ)ではなく、new BigDecimal("0.1")(文字列コンストラクタ)を使うのが鉄則です。doubleコンストラクタでは浮動小数点の誤差がそのまま入ってしまいます。
doubleコンストラクタの罠
BigDecimalTrap.java
import java.math.BigDecimal;
public class BigDecimalTrap {
public static void main(String[] args) {
// NG: doubleコンストラクタ(誤差が混入)
BigDecimal bad = new BigDecimal(0.1);
System.out.println("double: " + bad);
// OK: 文字列コンストラクタ(正確)
BigDecimal good = new BigDecimal("0.1");
System.out.println("String: " + good);
// OK: valueOf() もdoubleから安全に変換可能
BigDecimal safe = BigDecimal.valueOf(0.1);
System.out.println("valueOf: " + safe);
}
}
実行結果
double: 0.1000000000000000055511151231257827021181583404541015625
String: 0.1
valOf: 0.1
RoundingMode の各種丸めモード
Java の java.math.RoundingMode には8種類の丸めモードが用意されています。用途に応じて適切なモードを選択しましょう。
| モード |
説明 |
2.5の結果 |
-2.5の結果 |
HALF_UP |
一般的な四捨五入(0.5は切り上げ) |
3 |
-3 |
HALF_DOWN |
五捨六入(0.5は切り捨て) |
2 |
-2 |
HALF_EVEN |
銀行丸め(0.5は偶数方向に丸める) |
2 |
-2 |
CEILING |
正の無限大方向に切り上げ |
3 |
-2 |
FLOOR |
負の無限大方向に切り捨て |
2 |
-3 |
UP |
ゼロから離れる方向に切り上げ |
3 |
-3 |
DOWN |
ゼロに近づく方向に切り捨て |
2 |
-2 |
UNNECESSARY |
丸め不要を表明(端数があるとエラー) |
例外 |
例外 |
各モードの動作を確認するコード
RoundingModeDemo.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class RoundingModeDemo {
public static void main(String[] args) {
BigDecimal val = new BigDecimal("2.55");
System.out.println("HALF_UP: " + val.setScale(1, RoundingMode.HALF_UP));
System.out.println("HALF_DOWN: " + val.setScale(1, RoundingMode.HALF_DOWN));
System.out.println("HALF_EVEN: " + val.setScale(1, RoundingMode.HALF_EVEN));
System.out.println("CEILING: " + val.setScale(1, RoundingMode.CEILING));
System.out.println("FLOOR: " + val.setScale(1, RoundingMode.FLOOR));
System.out.println("UP: " + val.setScale(1, RoundingMode.UP));
System.out.println("DOWN: " + val.setScale(1, RoundingMode.DOWN));
}
}
実行結果
HALF_UP: 2.6
HALF_DOWN: 2.5
HALF_EVEN: 2.6
CEILING: 2.6
FLOOR: 2.5
UP: 2.6
DOWN: 2.5
ポイント:HALF_EVEN(銀行丸め)は、大量のデータを集計するときに合計の偏りが最も小さくなるモードです。統計処理や科学計算で推奨されています。
DecimalFormat での書式付き四捨五入
DecimalFormat を使うと、四捨五入と同時に表示形式の指定ができます。カンマ区切りやゼロ埋めが必要な場合に便利です。
DecimalFormatDemo.java
import java.text.DecimalFormat;
import java.math.RoundingMode;
public class DecimalFormatDemo {
public static void main(String[] args) {
double price = 12345.6789;
// 小数第2位まで表示(四捨五入)
DecimalFormat df1 = new DecimalFormat("#.##");
System.out.println(df1.format(price)); // 12345.68
// カンマ区切り + 小数第2位まで(ゼロ埋め)
DecimalFormat df2 = new DecimalFormat("#,##0.00");
System.out.println(df2.format(price)); // 12,345.68
// RoundingModeを指定
DecimalFormat df3 = new DecimalFormat("#.##");
df3.setRoundingMode(RoundingMode.DOWN); // 切り捨て
System.out.println(df3.format(price)); // 12345.67
// パーセント表示
DecimalFormat df4 = new DecimalFormat("#.#%");
System.out.println(df4.format(0.8567)); // 85.7%
}
}
実行結果
12345.68
12,345.68
12345.67
85.7%
| パターン |
意味 |
例(入力: 1234.5) |
#.## |
最大小数2桁(末尾の0は非表示) |
1234.5 |
0.00 |
必ず小数2桁表示(ゼロ埋め) |
1234.50 |
#,##0.00 |
カンマ区切り + 小数2桁 |
1,234.50 |
#.#% |
パーセント表示(小数1桁) |
123450.0% |
Math.ceil() / Math.floor() との違い
Math.round() と混同しやすい Math.ceil()(切り上げ)と Math.floor()(切り捨て)の違いを整理します。
| メソッド |
動作 |
戻り値型 |
3.2 |
3.7 |
-3.2 |
-3.7 |
Math.round() |
四捨五入 |
long/int |
3 |
4 |
-3 |
-4 |
Math.ceil() |
切り上げ(正の無限大方向) |
double |
4.0 |
4.0 |
-3.0 |
-3.0 |
Math.floor() |
切り捨て(負の無限大方向) |
double |
3.0 |
3.0 |
-4.0 |
-4.0 |
CeilFloorCompare.java
public class CeilFloorCompare {
public static void main(String[] args) {
double value = 3.3;
System.out.println("round: " + Math.round(value)); // 3
System.out.println("ceil: " + Math.ceil(value)); // 4.0
System.out.println("floor: " + Math.floor(value)); // 3.0
double negative = -3.3;
System.out.println("round: " + Math.round(negative)); // -3
System.out.println("ceil: " + Math.ceil(negative)); // -3.0
System.out.println("floor: " + Math.floor(negative)); // -4.0
}
}
実行結果
round: 3
ceil: 4.0
floor: 3.0
round: -3
ceil: -3.0
floor: -4.0
使い分けの目安
- Math.round():一般的な四捨五入(最も近い整数)
- Math.ceil():切り上げ(ページ数計算、必要な箱の数など)
- Math.floor():切り捨て(配分可能な個数など)
浮動小数点の罠(0.1 + 0.2 問題)
Javaの double / float はIEEE 754 浮動小数点数で表現されるため、10進数を正確に表現できない場合があります。これは四捨五入処理で予期せぬ結果を引き起こすことがあります。
FloatingPointTrap.java
public class FloatingPointTrap {
public static void main(String[] args) {
// 0.1 + 0.2 は 0.3 にならない!
double result = 0.1 + 0.2;
System.out.println("0.1 + 0.2 = " + result);
// 四捨五入にも影響する
double val = 2.675;
double rounded = Math.round(val * 100) / 100.0;
System.out.println("2.675の小数第2位四捨五入: " + rounded);
// 期待: 2.68 → 実際: 2.67(誤差のため)
// doubleの実際の値を確認
System.out.println("2.675の実体: " +
new java.math.BigDecimal(2.675));
}
}
実行結果
0.1 + 0.2 = 0.30000000000000004
2.675の小数第2位四捨五入: 2.67
2.675の実体: 2.674999999999999955591079014993738383054733276367187500
注意:2.675 は double 内部では 2.6749999... として表現されるため、小数第3位が「4」と判定されて切り捨てされます。このような問題を防ぐには、BigDecimal の文字列コンストラクタを使いましょう。
BigDecimal で正しく四捨五入
CorrectRounding.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class CorrectRounding {
public static void main(String[] args) {
// 文字列から生成すれば正確
BigDecimal bd = new BigDecimal("2.675");
BigDecimal rounded = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(rounded); // 2.68(正しい)
}
}
通貨計算での注意点
金額を扱う処理では1円の誤差も許されないため、必ず BigDecimal を使います。double での金額計算は絶対に避けましょう。
税計算の例
TaxCalculation.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class TaxCalculation {
public static void main(String[] args) {
BigDecimal price = new BigDecimal("1980");
BigDecimal taxRate = new BigDecimal("0.10"); // 消費税10%
// 税額を計算(小数以下切り捨て)
BigDecimal tax = price.multiply(taxRate)
.setScale(0, RoundingMode.DOWN);
System.out.println("税額(切り捨て): " + tax + "円");
// 税込み価格
BigDecimal total = price.add(tax);
System.out.println("税込価格: " + total + "円");
// 税額を四捨五入する場合
BigDecimal taxRound = price.multiply(taxRate)
.setScale(0, RoundingMode.HALF_UP);
System.out.println("税額(四捨五入): " + taxRound + "円");
}
}
実行結果
税額(切り捨て): 198円
税込価格: 2178円
税額(四捨五入): 198円
通貨計算の鉄則
- double/float を使わない:必ず BigDecimal を使う
- 文字列コンストラクタを使う:
new BigDecimal("0.1")
- 丸めモードを明示する:日本の消費税計算は一般に切り捨て(DOWN)
- 計算順序に注意:掛け算 → 丸め の順で処理する
String.format() での丸め
String.format() を使うと、四捨五入した結果をフォーマットされた文字列として取得できます。表示目的の丸めに便利です。
StringFormatDemo.java
public class StringFormatDemo {
public static void main(String[] args) {
double pi = 3.14159265;
// 小数第2位まで(四捨五入)
System.out.println(String.format("%.2f", pi)); // 3.14
// 小数第4位まで
System.out.println(String.format("%.4f", pi)); // 3.1416
// 整数にフォーマット
System.out.println(String.format("%.0f", pi)); // 3
// 幅指定(右寄せ10桁)
System.out.println(String.format("%10.2f", pi)); // 3.14
// printf でも同じ書式が使える
System.out.printf("円周率: %.3f%n", pi); // 円周率: 3.142
}
}
実行結果
3.14
3.1416
3
3.14
円周率: 3.142
ポイント:String.format() は HALF_UP(一般的な四捨五入)で丸められます。表示のみが目的の場合はこの方法が最も手軽です。ただし、結果は文字列なので計算には使えません。
よくあるエラーと対処法
1. ArithmeticException: Rounding necessary
setScale() で RoundingMode.UNNECESSARY を指定したとき、丸めが必要な値を渡すと発生します。
ArithmeticError.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class ArithmeticError {
public static void main(String[] args) {
BigDecimal bd = new BigDecimal("3.14");
// NG: 丸めが必要なのにUNNECESSARYを指定
try {
bd.setScale(1, RoundingMode.UNNECESSARY);
} catch (ArithmeticException e) {
System.out.println("エラー: " + e.getMessage());
}
// OK: 適切な丸めモードを指定
BigDecimal result = bd.setScale(1, RoundingMode.HALF_UP);
System.out.println("正常: " + result);
}
}
実行結果
エラー: Rounding necessary
正常: 3.1
2. 非推奨の ROUND_* 定数
Java 9以降では BigDecimal.ROUND_HALF_UP などのint定数は非推奨です。代わりに RoundingMode 列挙型を使いましょう。
非推奨 vs 推奨
// NG: 非推奨(Java 9以降で警告)
bd.setScale(2, BigDecimal.ROUND_HALF_UP);
// OK: 推奨
bd.setScale(2, RoundingMode.HALF_UP);
3. Math.round() の負数の挙動
Math.round() は内部で Math.floor(a + 0.5) を行うため、負の数の.5は0に近い方に丸められます。
NegativeRound.java
public class NegativeRound {
public static void main(String[] args) {
System.out.println(Math.round(2.5)); // 3 (0.5は切り上げ)
System.out.println(Math.round(-2.5)); // -2 (0に近い方に丸まる)
System.out.println(Math.round(-3.5)); // -3 (0に近い方に丸まる)
}
}
注意:負の数で「0.5を常に絶対値が大きい方に丸めたい」場合は、BigDecimal と RoundingMode.HALF_UP を使ってください。
実務パターン(税計算・成績計算・割引計算)
消費税計算ユーティリティ
TaxUtil.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class TaxUtil {
private static final BigDecimal TAX_RATE_10 =
new BigDecimal("0.10"); // 標準税率10%
private static final BigDecimal TAX_RATE_8 =
new BigDecimal("0.08"); // 軽減税率8%
/** 税込価格を計算(端数切り捨て) */
public static BigDecimal calcTaxIncluded(BigDecimal price, boolean reduced) {
BigDecimal rate = reduced ? TAX_RATE_8 : TAX_RATE_10;
BigDecimal tax = price.multiply(rate)
.setScale(0, RoundingMode.DOWN);
return price.add(tax);
}
public static void main(String[] args) {
BigDecimal price = new BigDecimal("2980");
System.out.println("標準税率: " + calcTaxIncluded(price, false) + "円");
System.out.println("軽減税率: " + calcTaxIncluded(price, true) + "円");
}
}
実行結果
標準税率: 3278円
軽減税率: 3218円
成績計算(平均点の四捨五入)
GradeCalculation.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class GradeCalculation {
public static void main(String[] args) {
int[] scores = {85, 92, 78, 96, 63};
// 合計を計算
BigDecimal sum = BigDecimal.ZERO;
for (int score : scores) {
sum = sum.add(new BigDecimal(score));
}
// 平均を計算(小数第1位で四捨五入)
BigDecimal count = new BigDecimal(scores.length);
BigDecimal avg = sum.divide(count, 1, RoundingMode.HALF_UP);
System.out.println("合計: " + sum + "点");
System.out.println("平均: " + avg + "点");
}
}
割引計算
DiscountCalc.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class DiscountCalc {
public static void main(String[] args) {
BigDecimal original = new BigDecimal("3980");
BigDecimal discountRate = new BigDecimal("0.30"); // 30%OFF
// 割引額(端数切り捨て)
BigDecimal discount = original.multiply(discountRate)
.setScale(0, RoundingMode.DOWN);
// 割引後価格
BigDecimal discounted = original.subtract(discount);
System.out.println("元の価格: " + original + "円");
System.out.println("割引額: " + discount + "円");
System.out.println("割引後: " + discounted + "円");
}
}
実行結果
元の価格: 3980円
割引額: 1194円
割引後: 2786円
四捨五入メソッドの比較まとめ
ここまで紹介した方法を、用途別に比較します。
| 方法 |
精度 |
桁指定 |
丸めモード |
おすすめ用途 |
Math.round() |
低 |
整数のみ |
HALF_UP相当 |
簡易な整数化 |
| 10の累乗 + round |
中 |
任意 |
HALF_UP相当 |
プロトタイプ |
BigDecimal |
高 |
任意 |
8種類 |
金額・業務ロジック |
DecimalFormat |
中 |
任意 |
指定可 |
表示フォーマット |
String.format() |
中 |
任意 |
HALF_UP |
文字列出力 |
ポイント:迷ったら BigDecimal を使えば間違いありません。特に金額計算や業務ロジックでは、BigDecimal一択です。
まとめ
Javaにおける四捨五入の方法を一通り解説しました。最後に重要なポイントを整理します。
この記事のまとめ
- Math.round() は整数への四捨五入に最適(float→int, double→long)
- 小数第N位で丸めるには、10の累乗を掛けて round して割る
- BigDecimal + setScale() で精密な四捨五入が可能(実務では必須)
- RoundingMode で8種類の丸めモードを使い分けられる
- DecimalFormat / String.format() は表示フォーマットに便利
- 浮動小数点の誤差に注意:2.675の四捨五入で2.67になる罠がある
- 通貨計算では必ず BigDecimal を使い、doubleは避ける
- BigDecimalは文字列コンストラクタ(
"0.1")を使うのが鉄則
ポイント:学習段階では Math.round() から始めて、実務では BigDecimal に移行するのがおすすめです。特に金額を扱うコードを書くときは、最初から BigDecimal を使う習慣をつけましょう。