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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?