- JAVA
Java 割り算の小数・切り捨て・余りまとめ|整数除算と0除算の挙動
Java の割り算は、整数どうし(int / int)だと小数点以下が切り捨てられて整数になり、どちらか一方が小数(double)なら小数の結果になります。 7 / 2 は 3、7.0 / 2 は 3.5 です。小数で結果がほしいときは、割る前に片方を double にします。余りは % で求め、0 で割ると整数では ArithmeticException、小数では Infinity や NaN になります。
この記事では、正の数だけでなく負の数・余り・0除算・あふれ・丸め・BigDecimal まで、割り算でつまずきやすい点を1本にまとめました。25 の式を並べた早見表をはじめ、掲載した出力はすべて実際に動かした結果です。
確認環境:Ubuntu 24.04、OpenJDK 21.0.10(java ファイル名.java の単一ファイル実行)。
Java の割り算 早見表(式と結果)
| 式 | 結果 | ポイント |
|---|---|---|
7 / 2 |
3 | int どうしは小数点以下を切り捨て |
-7 / 2 |
-3 | 0 に近い方へ丸める(-4 にはならない) |
7 / -2 |
-3 | 同上 |
-7 / -2 |
3 | 同上 |
1 / 3 |
0 | 割られる数の方が小さいと 0 |
7 % 2 |
1 | 余り |
-7 % 2 |
-1 | 余りの符号は「割られる数」と同じ |
7 % -2 |
1 | 割る数の符号は結果に影響しない |
-7 % -2 |
-1 | 同上 |
7.0 / 2 |
3.5 | 片方が double なら結果も double |
7 / 2.0 |
3.5 | 同上 |
(double) 7 / 2 |
3.5 | 7 を double にしてから割る |
(double) (7 / 2) |
3.0 | 割った後(3)を double にしている |
7 / 2 * 1.0 |
3.0 | 左から計算するので、先に 3 になる |
1.0 * 7 / 2 |
3.5 | 先に double になる |
7L / 2 |
3 | long でも整数どうしは切り捨て |
10.0 / 3 |
3.3333333333333335 | double は約16桁 |
10f / 3 |
3.3333333 | float は約7桁 |
7.5 % 2 |
1.5 | 小数にも % が使える |
10 / 0 |
ArithmeticException: / by zero | 整数の0除算は例外 |
10.0 / 0 |
Infinity | 小数の0除算は例外にならない |
0.0 / 0 |
NaN | 「数ではない」を表す値 |
Math.floorDiv(-7, 2) |
-4 | 小さい方(負の無限大の方向)へ丸める |
Math.floorMod(-7, 2) |
1 | 余りの符号が「割る数」と同じ |
Math.ceilDiv(7, 2) |
4 | 切り上げ(Java 18 以降) |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java 割り算の小数・切り捨て・余りまとめ|整数除算と0除算の挙動」
早見表の式をまとめて実行するコード
早見表の上から 19 行と、キャストや平均の例を1つのプログラムで確かめます。java DivTable.java でそのまま実行できます。
public class DivTable {
public static void main(String[] args) {
System.out.println("7 / 2 = " + (7 / 2));
System.out.println("-7 / 2 = " + (-7 / 2));
System.out.println("7 / -2 = " + (7 / -2));
System.out.println("-7 / -2 = " + (-7 / -2));
System.out.println("1 / 3 = " + (1 / 3));
System.out.println("7 % 2 = " + (7 % 2));
System.out.println("-7 % 2 = " + (-7 % 2));
System.out.println("7 % -2 = " + (7 % -2));
System.out.println("-7 % -2 = " + (-7 % -2));
System.out.println("7.0 / 2 = " + (7.0 / 2));
System.out.println("7 / 2.0 = " + (7 / 2.0));
System.out.println("(double) 7 / 2 = " + ((double) 7 / 2));
System.out.println("(double) (7 / 2) = " + ((double) (7 / 2)));
System.out.println("7 / 2 * 1.0 = " + (7 / 2 * 1.0));
System.out.println("1.0 * 7 / 2 = " + (1.0 * 7 / 2));
System.out.println("7f / 2 = " + (7f / 2));
System.out.println("7L / 2 = " + (7L / 2));
System.out.println("10.0 / 3 = " + (10.0 / 3));
System.out.println("10f / 3 = " + (10f / 3));
System.out.println("7.5 % 2 = " + (7.5 % 2));
System.out.println("-7.5 % 2 = " + (-7.5 % 2));
System.out.println("(int) 3.99 = " + ((int) 3.99));
System.out.println("(int) -3.99 = " + ((int) -3.99));
double avg = (80 + 75 + 90 + 68) / 4;
System.out.println("double avg = (80 + 75 + 90 + 68) / 4; -> " + avg);
double avg2 = (80 + 75 + 90 + 68) / 4.0;
System.out.println("double avg2 = (80 + 75 + 90 + 68) / 4.0; -> " + avg2);
}
}
7 / 2 = 3
-7 / 2 = -3
7 / -2 = -3
-7 / -2 = 3
1 / 3 = 0
7 % 2 = 1
-7 % 2 = -1
7 % -2 = 1
-7 % -2 = -1
7.0 / 2 = 3.5
7 / 2.0 = 3.5
(double) 7 / 2 = 3.5
(double) (7 / 2) = 3.0
7 / 2 * 1.0 = 3.0
1.0 * 7 / 2 = 3.5
7f / 2 = 3.5
7L / 2 = 3
10.0 / 3 = 3.3333333333333335
10f / 3 = 3.3333333
7.5 % 2 = 1.5
-7.5 % 2 = -1.5
(int) 3.99 = 3
(int) -3.99 = -3
double avg = (80 + 75 + 90 + 68) / 4; -> 78.0
double avg2 = (80 + 75 + 90 + 68) / 4.0; -> 78.25
整数どうしの割り算で小数が出ない理由
int / int の結果は int になる
Java では、演算の結果の型は、計算する値の型で決まります。int と int の割り算は int の計算として行われ、結果も int です。小数点以下を持てないため、7 / 2 は 3.5 ではなく 3 になります。
| 左の型 | 右の型 | 結果の型 | 7 と 2 の例 |
|---|---|---|---|
| int | int | int | 7 / 2 → 3 |
| long | int | long | 7L / 2 → 3 |
| double | int | double | 7.0 / 2 → 3.5 |
| int | double | double | 7 / 2.0 → 3.5 |
| float | int | float | 7f / 2 → 3.5 |
代入先の変数を double にしても、右辺の計算が先に int で終わっていると小数は戻りません。4人の点数の平均を求める例で確かめます(上のコードの最後の部分)。
1つ目は 313 / 4 が先に 78 になるため 78.0、2つ目は 4.0 が double なので 78.25 です。
キャストの位置で結果が変わる
変数どうしを割るときは 4.0 のように書けないので、キャスト(型変換)を使います。かっこの位置が結果を分けます。
| 書き方 | 結果 | 計算の順番 |
|---|---|---|
(double) a / b |
3.5 | a を double にしてから割る |
a / (double) b |
3.5 | b を double にしてから割る |
(double) (a / b) |
3.0 | a / b を int で計算(3)してから double にする |
1.0 * a / b |
3.5 | 先頭で double になる |
a / b * 1.0 |
3.0 | a / b が先に 3 になる |
(a が 7、b が 2 の場合です。)キャストは割り算より先に、片方の値だけに付けると覚えておくと迷いません。
小数の結果を int の変数に入れるとコンパイルエラーになる
逆に、小数の計算結果を int の変数にそのまま代入すると、コンパイルの段階で知らせてくれます。
public class CompileErr {
public static void main(String[] args) {
int half = 7 / 2.0;
System.out.println(half);
}
}
$ java CompileErr.java
CompileErr.java:3: error: incompatible types: possible lossy conversion from double to int
int half = 7 / 2.0;
^
1 error
error: compilation failed
小数点以下を捨ててよい場合は、int half = (int) (7 / 2.0); のように明示的にキャストします。(int) は 0 に近い方へ丸めるので、(int) 3.99 は 3、(int) -3.99 は -3 です。
負の数の割り算と余り
/ は 0 に近い方へ丸め、% の符号は割られる数に合わせる
Java 言語仕様(15.17.2、15.17.3)では、整数の割り算は 0 に近い方へ丸めること、余りの符号は割られる数(左側)の符号と同じになることが定められています。実行結果もそのとおりです。
| 式 | 商(/) | 余り(%) | 検算 商×割る数+余り |
|---|---|---|---|
| 7 と 2 | 3 | 1 | 3×2+1=7 |
| -7 と 2 | -3 | -1 | -3×2+(-1)=-7 |
| 7 と -2 | -3 | 1 | -3×(-2)+1=7 |
| -7 と -2 | 3 | -1 | 3×(-2)+(-1)=-7 |
「切り捨て」という言葉から -7 / 2 が -4 になると考えがちですが、Java の / は -3.5 を 0 に近い -3 にします。-4 がほしいときは、次の Math.floorDiv を使います。
Math.floorDiv・floorMod・ceilDiv の違い
Math クラスには、丸める方向を選べる割り算のメソッドがあります。Java SE 21 の API ドキュメントによると、floorDiv と floorMod は Java 8 から、ceilDiv と ceilMod は Java 18 から使えます。
public class FloorDemo {
public static void main(String[] args) {
int[][] pairs = { {7, 2}, {-7, 2}, {7, -2}, {-7, -2}, {6, 2}, {-6, 2} };
System.out.println(" a b | a/b floorDiv ceilDiv | a%b floorMod ceilMod");
for (int[] p : pairs) {
int a = p[0], b = p[1];
System.out.printf("%3d %3d | %4d %8d %7d | %4d %8d %7d%n",
a, b,
a / b, Math.floorDiv(a, b), Math.ceilDiv(a, b),
a % b, Math.floorMod(a, b), Math.ceilMod(a, b));
}
}
}
a b | a/b floorDiv ceilDiv | a%b floorMod ceilMod
7 2 | 3 3 4 | 1 1 -1
-7 2 | -3 -4 -3 | -1 1 -1
7 -2 | -3 -4 -3 | 1 -1 1
-7 -2 | 3 3 4 | -1 -1 1
6 2 | 3 3 3 | 0 0 0
-6 2 | -3 -3 -3 | 0 0 0
| 書き方 | 丸める方向 | -7 と 2 | 余りの符号 |
|---|---|---|---|
a / b、a % b |
0 に近い方 | 商 -3、余り -1 | 割られる数と同じ |
Math.floorDiv、Math.floorMod |
小さい方(負の無限大の方向) | 商 -4、余り 1 | 割る数と同じ |
Math.ceilDiv、Math.ceilMod |
大きい方(正の無限大の方向) | 商 -3、余り -1 | 割る数と逆 |
正の数どうしなら / と floorDiv は同じ結果です。違いが出るのは、割り切れず、かつ符号が異なる組み合わせのときだけです。割り切れる 6 / 2 や -6 / 2 は、3つとも同じ値になっています。
余り(%)の使い方と、負の数での注意
余りは、奇数・偶数の判定、一周して戻る番号、単位の換算などに使います。
public class ModUse {
public static void main(String[] args) {
// 奇数判定: 負の数でも正しく動く書き方は「!= 0」
for (int n : new int[] {3, -3, 4}) {
System.out.println(n + " : n % 2 == 1 -> " + (n % 2 == 1)
+ " / n % 2 != 0 -> " + (n % 2 != 0));
}
// 配列の添字を一周させる: 左に戻るときは floorMod
String[] days = {"日", "月", "火", "水", "木", "金", "土"};
int today = 1; // 月
int back = today - 3;
System.out.println("(today - 3) % 7 = " + (back % 7));
System.out.println("Math.floorMod(today - 3, 7) = " + Math.floorMod(back, 7)
+ " -> " + days[Math.floorMod(back, 7)]);
// ページ数: 件数を1ページの件数で割って切り上げる
int total = 101, perPage = 20;
System.out.println("101 / 20 = " + (total / perPage));
System.out.println("(101 + 20 - 1) / 20 = " + ((total + perPage - 1) / perPage));
System.out.println("Math.ceilDiv(101, 20) = " + Math.ceilDiv(total, perPage));
System.out.println("Math.ceilDiv(100, 20) = " + Math.ceilDiv(100, perPage));
// 秒を「分と秒」に分ける
int sec = 200;
System.out.println(sec + "秒 = " + (sec / 60) + "分" + (sec % 60) + "秒");
}
}
3 : n % 2 == 1 -> true / n % 2 != 0 -> true
-3 : n % 2 == 1 -> false / n % 2 != 0 -> true
4 : n % 2 == 1 -> false / n % 2 != 0 -> false
(today - 3) % 7 = -2
Math.floorMod(today - 3, 7) = 5 -> 金
101 / 20 = 5
(101 + 20 - 1) / 20 = 6
Math.ceilDiv(101, 20) = 6
Math.ceilDiv(100, 20) = 5
200秒 = 3分20秒
| やりたいこと | 書き方 | 理由 |
|---|---|---|
| 奇数かどうか | n % 2 != 0 |
n % 2 == 1 は、-3 のとき余りが -1 になるため false になる |
| 偶数かどうか | n % 2 == 0 |
符号に関係なく使える |
| 0〜6 の範囲で一周させる(戻る方向あり) | Math.floorMod(i, 7) |
% だと -2 のような負の値になり、配列の添字に使えない |
| ページ数(切り上げ) | Math.ceilDiv(total, perPage) |
(total + perPage - 1) / perPage でも同じ |
| 秒を分と秒に分ける | sec / 60 と sec % 60 |
商と余りをセットで使う |
0 で割るとどうなるか
整数の0除算は ArithmeticException: / by zero
public class ZeroInt {
public static void main(String[] args) {
int total = 10;
int count = 0;
System.out.println(total / count);
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ZeroInt.main(ZeroInt.java:5)
「/ by zero」は、整数を 0 で割ったことを表すメッセージです。% で 0 を指定したときも同じメッセージになります。なお、System.out.println(10 / 0); のように定数で書いた場合も、コンパイルは通り、実行時に同じ例外になることを確かめました。
型ごとの0除算の結果一覧
整数と小数では、0 で割ったときの動きがまったく異なります。
import java.math.BigDecimal;
public class ZeroAll {
interface Expr { Object eval(); }
static void show(String label, Expr e) {
try {
System.out.println(label + " = " + e.eval());
} catch (ArithmeticException ex) {
System.out.println(label + " -> " + ex);
}
}
public static void main(String[] args) {
int zero = 0;
long zeroL = 0L;
double zeroD = 0.0;
show("10 / 0 (int) ", () -> 10 / zero);
show("10 % 0 (int) ", () -> 10 % zero);
show("10L / 0L (long) ", () -> 10L / zeroL);
show("10.0 / 0 (double) ", () -> 10.0 / zero);
show("-10.0 / 0 (double) ", () -> -10.0 / zero);
show("0.0 / 0 (double) ", () -> 0.0 / zero);
show("10.0 % 0 (double) ", () -> 10.0 % zero);
show("10 / 0.0 ", () -> 10 / zeroD);
show("10f / 0 (float) ", () -> 10f / zero);
show("(int) (10.0 / 0) ", () -> (int) (10.0 / zero));
show("(int) (0.0 / 0) ", () -> (int) (0.0 / zero));
show("(long) (10.0 / 0) ", () -> (long) (10.0 / zero));
show("Math.floorDiv(10, 0)", () -> Math.floorDiv(10, zero));
show("Math.floorMod(10, 0)", () -> Math.floorMod(10, zero));
show("BigDecimal 10 / 0 ", () -> BigDecimal.TEN.divide(BigDecimal.ZERO));
show("BigDecimal 0 / 0 ", () -> BigDecimal.ZERO.divide(BigDecimal.ZERO));
double inf = 10.0 / zero;
double nan = 0.0 / zero;
System.out.println("Double.isInfinite(inf) = " + Double.isInfinite(inf));
System.out.println("Double.isNaN(nan) = " + Double.isNaN(nan));
System.out.println("nan == nan = " + (nan == nan));
System.out.println("Double.isFinite(7.0/2) = " + Double.isFinite(7.0 / 2));
System.out.println("inf + 1 = " + (inf + 1));
System.out.println("inf - inf = " + (inf - inf));
}
}
10 / 0 (int) -> java.lang.ArithmeticException: / by zero
10 % 0 (int) -> java.lang.ArithmeticException: / by zero
10L / 0L (long) -> java.lang.ArithmeticException: / by zero
10.0 / 0 (double) = Infinity
-10.0 / 0 (double) = -Infinity
0.0 / 0 (double) = NaN
10.0 % 0 (double) = NaN
10 / 0.0 = Infinity
10f / 0 (float) = Infinity
(int) (10.0 / 0) = 2147483647
(int) (0.0 / 0) = 0
(long) (10.0 / 0) = 9223372036854775807
Math.floorDiv(10, 0) -> java.lang.ArithmeticException: / by zero
Math.floorMod(10, 0) -> java.lang.ArithmeticException: / by zero
BigDecimal 10 / 0 -> java.lang.ArithmeticException: Division by zero
BigDecimal 0 / 0 -> java.lang.ArithmeticException: Division undefined
Double.isInfinite(inf) = true
Double.isNaN(nan) = true
nan == nan = false
Double.isFinite(7.0/2) = true
inf + 1 = Infinity
inf - inf = NaN
| 計算 | 結果 |
|---|---|
int・long を 0 で割る(/、%、floorDiv、floorMod) |
ArithmeticException: / by zero |
| double・float の正の数を 0 で割る | Infinity(例外なし) |
| double・float の負の数を 0 で割る | -Infinity |
0.0 / 0、10.0 % 0 |
NaN |
| Infinity を int にキャスト | 2147483647(int の最大値) |
| NaN を int にキャスト | 0 |
| BigDecimal を 0 で割る | ArithmeticException: Division by zero |
| BigDecimal の 0 を 0 で割る | ArithmeticException: Division undefined |
小数の0除算は例外にならずに計算が続くため、気づかないまま Infinity や NaN が画面や集計に出てくることがあります。さらに int にキャストすると 2147483647 や 0 という「普通の数」に変わるので、原因がたどりにくくなります。普通の数かどうかは Double.isFinite(x)、NaN かどうかは Double.isNaN(x) で確かめます。NaN は x == x が false になるため、== では比べられません。
0除算を防ぐ書き方
基本は、割る前に割る数が 0 かどうかを確かめることです。件数が 0 のときの平均など、0 になりうる場面では既定値を決めておきます。
import java.util.OptionalDouble;
import java.util.stream.IntStream;
public class SafeDiv {
// 0で割るときは既定値を返す
static double average(int total, int count) {
if (count == 0) {
return 0.0;
}
return (double) total / count;
}
public static void main(String[] args) {
System.out.println(average(313, 4));
System.out.println(average(0, 0));
// 0件かどうかを OptionalDouble で受け取る
OptionalDouble avg = IntStream.of().average();
System.out.println(avg);
System.out.println(avg.orElse(0.0));
System.out.println(IntStream.of(80, 75, 90, 68).average().getAsDouble());
// try-catch で受ける書き方
try {
System.out.println(10 / Integer.parseInt("0"));
} catch (ArithmeticException e) {
System.out.println("割る数が0です: " + e.getMessage());
}
}
}
78.25
0.0
OptionalDouble.empty
0.0
78.25
割る数が0です: / by zero
0 のときの値が決まっているなら if で分け、「0件だった」ことを呼び出し元に伝えたいなら OptionalDouble、外から来た値にエラーの文言を出したいなら try-catch が向いています。
Integer.MIN_VALUE / -1 はあふれて元の値に戻る
0除算のほかに、整数の割り算で結果が合わなくなる組み合わせが1つだけあります。int の最小値を -1 で割る場合です。
public class Overflow {
public static void main(String[] args) {
int min = Integer.MIN_VALUE;
int minusOne = -1;
System.out.println("Integer.MIN_VALUE = " + min);
System.out.println("Integer.MIN_VALUE / -1 = " + (min / minusOne));
System.out.println("Integer.MIN_VALUE % -1 = " + (min % minusOne));
System.out.println("Math.floorDiv(MIN_VALUE,-1) = " + Math.floorDiv(min, minusOne));
System.out.println("Math.abs(MIN_VALUE) = " + Math.abs(min));
System.out.println("(long) MIN_VALUE / -1 = " + ((long) min / minusOne));
try {
System.out.println(Math.divideExact(min, minusOne));
} catch (ArithmeticException e) {
System.out.println("Math.divideExact(MIN_VALUE,-1) -> " + e);
}
}
}
Integer.MIN_VALUE = -2147483648
Integer.MIN_VALUE / -1 = -2147483648
Integer.MIN_VALUE % -1 = 0
Math.floorDiv(MIN_VALUE,-1) = -2147483648
Math.abs(MIN_VALUE) = -2147483648
(long) MIN_VALUE / -1 = 2147483648
Math.divideExact(MIN_VALUE,-1) -> java.lang.ArithmeticException: integer overflow
int の範囲は -2147483648 〜 2147483647 で、正の側が1つ少なくなっています。-2147483648 ÷ -1 の答え 2147483648 は int に収まらず、あふれて元の値に戻ります。Java 言語仕様(15.17.2)にも、この場合はあふれが起きて結果が割られる数と等しくなり、例外は投げられないと書かれています。
| 対応 | 書き方 |
|---|---|
| long で計算する | (long) a / b |
| あふれを例外で知りたい | Math.divideExact(a, b)(Java 18 以降。integer overflow の ArithmeticException) |
符号を反転する Math.abs(Integer.MIN_VALUE) も同じ理由で負のままになります。入力として最小値が来うる計算では、long で受けるか、Exact 系のメソッドを使うと気づけます。
切り捨て・切り上げ・四捨五入・小数第2位までの求め方
丸めのメソッドの違い
小数を整数に丸めるメソッドは、負の数と「ちょうど .5」のときに違いが出ます。
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Rounding {
public static void main(String[] args) {
double[] values = { 3.5, 2.5, -2.5, 3.14159, -3.7 };
System.out.println(" x | (int) floor ceil round rint");
for (double x : values) {
System.out.printf("%7s | %5d %5.1f %5.1f %5d %5.1f%n",
x, (int) x, Math.floor(x), Math.ceil(x), Math.round(x), Math.rint(x));
}
// 整数どうしの割り算で、切り捨て・切り上げ・四捨五入
int a = 7, b = 2;
System.out.println("切り捨て 7 / 2 = " + (a / b));
System.out.println("切り上げ Math.ceilDiv(7, 2) = " + Math.ceilDiv(a, b));
System.out.println("四捨五入 Math.round((double) 7 / 2) = " + Math.round((double) a / b));
System.out.println("四捨五入 (7 + 2 / 2) / 2 = " + ((a + b / 2) / b));
// 小数第2位まで
double d = 10.0 / 3;
System.out.println("10.0 / 3 = " + d);
System.out.println("String.format(\"%.2f\", d) = " + String.format("%.2f", d));
System.out.println("Math.round(d * 100) / 100.0 = " + (Math.round(d * 100) / 100.0));
System.out.println("Math.floor(d * 100) / 100 = " + (Math.floor(d * 100) / 100));
System.out.println("Math.ceil(d * 100) / 100 = " + (Math.ceil(d * 100) / 100));
System.out.println("Math.round(d * 100) / 100 = " + (Math.round(d * 100) / 100));
BigDecimal bd = new BigDecimal("10").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);
System.out.println("BigDecimal 10 / 3 (2桁, HALF_UP) = " + bd);
// double の誤差が見える例
System.out.println("1.005 * 100 = " + (1.005 * 100));
System.out.println("Math.round(1.005 * 100) / 100.0 = " + (Math.round(1.005 * 100) / 100.0));
System.out.println("BigDecimal(\"1.005\") 2桁 HALF_UP = "
+ new BigDecimal("1.005").setScale(2, RoundingMode.HALF_UP));
System.out.println("0.1 + 0.2 = " + (0.1 + 0.2));
}
}
x | (int) floor ceil round rint
3.5 | 3 3.0 4.0 4 4.0
2.5 | 2 2.0 3.0 3 2.0
-2.5 | -2 -3.0 -2.0 -2 -2.0
3.14159 | 3 3.0 4.0 3 3.0
-3.7 | -3 -4.0 -3.0 -4 -4.0
切り捨て 7 / 2 = 3
切り上げ Math.ceilDiv(7, 2) = 4
四捨五入 Math.round((double) 7 / 2) = 4
四捨五入 (7 + 2 / 2) / 2 = 4
10.0 / 3 = 3.3333333333333335
String.format("%.2f", d) = 3.33
Math.round(d * 100) / 100.0 = 3.33
Math.floor(d * 100) / 100 = 3.33
Math.ceil(d * 100) / 100 = 3.34
Math.round(d * 100) / 100 = 3
BigDecimal 10 / 3 (2桁, HALF_UP) = 3.33
1.005 * 100 = 100.49999999999999
Math.round(1.005 * 100) / 100.0 = 1.0
BigDecimal("1.005") 2桁 HALF_UP = 1.01
0.1 + 0.2 = 0.30000000000000004
| メソッド | 動き | 2.5 | -2.5 | -3.7 |
|---|---|---|---|---|
(int) x |
0 に近い方へ(小数点以下を捨てる) | 2 | -2 | -3 |
Math.floor(x) |
小さい方へ | 2.0 | -3.0 | -4.0 |
Math.ceil(x) |
大きい方へ | 3.0 | -2.0 | -3.0 |
Math.round(x) |
四捨五入(.5 は大きい方へ)。戻り値は long | 3 | -2 | -4 |
Math.rint(x) |
最も近い整数。.5 は偶数の方へ | 2.0 | -2.0 | -4.0 |
整数どうしの割り算は、切り上げなら Math.ceilDiv(7, 2)、四捨五入なら Math.round((double) 7 / 2) で、どちらも 4 になります。
小数第2位まで求める
10.0 / 3 を小数第2位までにする方法を並べます。
| 目的 | 書き方 | 結果 |
|---|---|---|
| 表示だけ2桁にする(四捨五入) | String.format("%.2f", d) |
3.33(文字列) |
| 数値として四捨五入 | Math.round(d * 100) / 100.0 |
3.33 |
| 数値として切り捨て | Math.floor(d * 100) / 100 |
3.33 |
| 数値として切り上げ | Math.ceil(d * 100) / 100 |
3.34 |
| 桁を正確に扱う | BigDecimal の divide(b, 2, RoundingMode.HALF_UP) |
3.33 |
Math.round(d * 100) / 100 と 100.0 を 100 と書くと、long どうしの割り算になって 3 になります。ここでも「整数どうしは切り捨て」のルールが働きます。
また、double は2進数で値を持つため、10進数の小数をぴったり表せないことがあります。実行結果のとおり 1.005 * 100 は 100.49999999999999 になり、四捨五入すると 1.0 です。小数第何位かを正確に丸めたい計算では BigDecimal を使います。
BigDecimal の割り算と、割り切れないときのエラー
Non-terminating decimal expansion が出る原因
BigDecimal は 10 進数のまま計算できるクラスです。引数1つの divide は「正確な商」を返そうとするため、10 ÷ 3 のように割り切れない計算では例外になります。
import java.math.BigDecimal;
public class BigDiv {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
System.out.println(a.divide(b));
}
}
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1783)
at BigDiv.main(BigDiv.java:7)
メッセージは「小数が無限に続くため、正確に表せる結果がない」という意味です。API ドキュメントにも、正確な商が有限の小数で表せない場合に ArithmeticException を投げると書かれています。
直し方:桁数と丸め方を渡す
小数点以下の桁数(scale)と丸め方(RoundingMode)を引数で渡すと、その桁で丸めた結果が返ります。
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class BigDivFix {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
// 割り切れる組み合わせは、引数1つの divide でそのまま通る
System.out.println("10 / 4 = " + a.divide(new BigDecimal("4")));
// 直し方1: 小数点以下の桁数と丸め方を渡す
System.out.println("divide(b, 2, HALF_UP) = " + a.divide(b, 2, RoundingMode.HALF_UP));
System.out.println("divide(b, 2, DOWN) = " + a.divide(b, 2, RoundingMode.DOWN));
System.out.println("divide(b, 2, UP) = " + a.divide(b, 2, RoundingMode.UP));
System.out.println("divide(b, 0, DOWN) = " + a.divide(b, 0, RoundingMode.DOWN));
// 直し方2: 有効桁数で指定する(MathContext)
System.out.println("divide(b, DECIMAL32) = " + a.divide(b, MathContext.DECIMAL32));
System.out.println("divide(b, new MC(5)) = " + a.divide(b, new MathContext(5)));
// 丸め方を渡すだけの形は、割られる数の scale(ここでは0)に丸める
System.out.println("divide(b, HALF_UP) = " + a.divide(b, RoundingMode.HALF_UP));
// 商と余りを整数で求める
BigDecimal[] qr = a.divideAndRemainder(b);
System.out.println("divideAndRemainder = 商 " + qr[0] + " 余り " + qr[1]);
System.out.println("remainder = " + a.remainder(b));
System.out.println("divideToIntegralValue = " + new BigDecimal("-7").divideToIntegralValue(new BigDecimal("2")));
// 負の数の丸め方向
BigDecimal m = new BigDecimal("-7");
BigDecimal two = new BigDecimal("2");
for (RoundingMode mode : new RoundingMode[] {
RoundingMode.DOWN, RoundingMode.UP, RoundingMode.FLOOR,
RoundingMode.CEILING, RoundingMode.HALF_UP, RoundingMode.HALF_EVEN }) {
System.out.printf("-7 / 2 %-9s = %s 7 / 2 = %s%n", mode,
m.divide(two, 0, mode), new BigDecimal("7").divide(two, 0, mode));
}
// double から作るときは valueOf か文字列
System.out.println("new BigDecimal(0.1) = " + new BigDecimal(0.1));
System.out.println("BigDecimal.valueOf(0.1) = " + BigDecimal.valueOf(0.1));
System.out.println("new BigDecimal(\"0.1\") = " + new BigDecimal("0.1"));
}
}
10 / 4 = 2.5
divide(b, 2, HALF_UP) = 3.33
divide(b, 2, DOWN) = 3.33
divide(b, 2, UP) = 3.34
divide(b, 0, DOWN) = 3
divide(b, DECIMAL32) = 3.333333
divide(b, new MC(5)) = 3.3333
divide(b, HALF_UP) = 3
divideAndRemainder = 商 3 余り 1
remainder = 1
divideToIntegralValue = -3
-7 / 2 DOWN = -3 7 / 2 = 3
-7 / 2 UP = -4 7 / 2 = 4
-7 / 2 FLOOR = -4 7 / 2 = 3
-7 / 2 CEILING = -3 7 / 2 = 4
-7 / 2 HALF_UP = -4 7 / 2 = 4
-7 / 2 HALF_EVEN = -4 7 / 2 = 4
new BigDecimal(0.1) = 0.1000000000000000055511151231257827021181583404541015625
BigDecimal.valueOf(0.1) = 0.1
new BigDecimal("0.1") = 0.1
| 書き方 | 意味 | 10 ÷ 3 |
|---|---|---|
a.divide(b) |
正確な商。割り切れないと例外 | 例外 |
a.divide(b, 2, RoundingMode.HALF_UP) |
小数第2位まで、四捨五入 | 3.33 |
a.divide(b, 2, RoundingMode.UP) |
小数第2位まで、0 から遠い方へ | 3.34 |
a.divide(b, 0, RoundingMode.DOWN) |
整数に切り捨て | 3 |
a.divide(b, MathContext.DECIMAL32) |
有効桁数7桁 | 3.333333 |
a.divide(b, RoundingMode.HALF_UP) |
割られる数の scale(ここでは 0)に丸める | 3 |
a.divideAndRemainder(b) |
商と余りを配列で返す | 商 3、余り 1 |
RoundingMode は、負の数で違いが出ます。実行結果の -7 ÷ 2 を表にまとめます。
| RoundingMode | 動き | 7 ÷ 2 | -7 ÷ 2 | 対応する書き方 |
|---|---|---|---|---|
DOWN |
0 に近い方へ | 3 | -3 | int の / |
UP |
0 から遠い方へ | 4 | -4 | ― |
FLOOR |
小さい方へ | 3 | -4 | Math.floorDiv |
CEILING |
大きい方へ | 4 | -3 | Math.ceilDiv |
HALF_UP |
四捨五入(.5 は 0 から遠い方へ) | 4 | -4 | ― |
HALF_EVEN |
.5 は偶数の方へ | 4 | -4 | Math.rint |
BigDecimal を作るときは、文字列か BigDecimal.valueOf を使います。new BigDecimal(0.1) は double の持つ誤差ごと取り込み、0.1000000000000000055511151231257827021181583404541015625 になります。
なお、丸め方を int の定数で渡す divide(BigDecimal, int, int) は、API ドキュメントで Java 9 から非推奨とされています。RoundingMode を渡す形で書きます。
どの書き方を選ぶかの判断表
| やりたいこと | 選ぶ書き方 |
|---|---|
| 個数や回数など、整数の商がほしい | a / b |
| 割った結果を小数でほしい | (double) a / b |
| 余りがほしい | a % b |
| 負の数でも 0〜b-1 の範囲で余りがほしい | Math.floorMod(a, b) |
| 切り上げた商がほしい | Math.ceilDiv(a, b) |
| 四捨五入した整数がほしい | Math.round((double) a / b) |
| 表示を小数第2位までにしたい | String.format("%.2f", x) |
| 小数の桁と丸め方を正確に決めたい | BigDecimal の divide(b, 桁数, RoundingMode) |
| 割る数が 0 になりうる | 割る前に if (b == 0) で分ける |
| 最小値と -1 の組み合わせがありうる | (long) a / b か Math.divideExact(a, b) |
よくある質問
Q. double の変数に代入しているのに、結果が 3.0 になります。なぜですか?
A. 右辺の 7 / 2 が int どうしの計算として先に 3 になり、そのあと double に変換されているためです。7 / 2.0 や (double) a / b のように、割る前に片方を double にします。
Q. -7 / 2 が -4 ではなく -3 になるのはなぜですか?
A. Java の / は 0 に近い方へ丸めるためです。小さい方(-4)へ丸めたいときは Math.floorDiv(-7, 2) を使います。余りも % は -1、Math.floorMod は 1 と結果が変わります。
Q. 小数を 0 で割ったのに例外が出ません。どう確かめればよいですか?
A. double と float の0除算は Infinity か NaN になり、例外は出ません。Double.isFinite(x) で普通の数かどうかを確かめるか、割る前に割る数が 0 でないことを確かめます。
Q. BigDecimal の divide で Non-terminating decimal expansion が出ます。どう直しますか?
A. 割り切れない計算に引数1つの divide を使っているためです。a.divide(b, 2, RoundingMode.HALF_UP) のように、小数点以下の桁数と丸め方を渡します。
まとめ
- int どうしの割り算は小数点以下を切り捨てます(
7 / 2は 3)。小数がほしいときは(double) a / bのように、割る前に片方を double にします。 /は 0 に近い方へ丸め、%の符号は割られる数と同じです。-7 / 2は -3、-7 % 2は -1 になります。- 負の数で小さい方へ丸めたいときは Math.floorDiv と floorMod、切り上げは Math.ceilDiv(Java 18 以降)を使います。
- 整数の0除算は ArithmeticException: / by zero、小数の0除算は Infinity や NaN です。割る前に 0 かどうかを確かめます。
- Integer.MIN_VALUE / -1 はあふれて元の値に戻ります。long で計算するか Math.divideExact で気づけます。
- 小数第2位までは
String.format("%.2f", x)かMath.round(x * 100) / 100.0。100.0 を 100 と書くと整数どうしの割り算になります。 - BigDecimal の divide は、桁数と RoundingMode を渡すと Non-terminating decimal expansion を避けられます。
参考・出典
- The Java Language Specification, Java SE 21 Edition:15.17.2 Division Operator、15.17.3 Remainder Operator
- Java SE 21 API ドキュメント:java.lang.Math(floorDiv、floorMod、ceilDiv、ceilMod、divideExact)
- Java SE 21 API ドキュメント:java.math.BigDecimal(divide)
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java 割り算の小数・切り捨て・余りまとめ|整数除算と0除算の挙動」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。