整数の割り算で商と余りを求めるには
整数どうしの割り算で商と余りを求める基本形を各言語で示す。
負の数を割ったときに商を 0 の側へ切り捨てるか小さい側へ切り下げるかが言語ごとに違い、それに合わせて余りの符号も変わる点と、0 で割ったときの振る舞いの違いまでを扱う。
公開:
各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。
Python 実行確認済み
print(17 // 5, 17 % 5) # 3 2
print(divmod(17, 5)) # (3, 2)
print(17 / 5) # 3.4(/ は常に float を返す)
# 負の数では商を小さい側へ切り下げ、余りは割る数と同じ符号になる
print(-17 // 5, -17 % 5) # -4 3
print(divmod(-17, 5)) # (-4, 3)
try:
17 // 0
except ZeroDivisionError:
print("0 では割れない") # 0 では割れない// が商、% が余りを返し、divmod を使うと両方を 1 回で得られる。
// は商を小さい側へ切り下げるので -17 // 5 は -4 になり、余りは割る数と同じ符号の 3 になる。
/ は整数どうしでも float を返すので、整数の商が欲しいときは // を使う。
0 で割ると ZeroDivisionError が送出される。
JavaScript 実行確認済み
const a = 17, b = 5;
console.log(Math.trunc(a / b), a % b); // 3 2
console.log(a / b); // 3.4
// % の余りは割られる数と同じ符号になる
console.log(Math.trunc(-17 / 5), -17 % 5); // -3 -2
// 0 以上の余りが欲しいときは、割る数を足してからもう一度 % を取る
console.log(((-17 % 5) + 5) % 5); // 3
// 0 で割っても例外にはならない
console.log(17 / 0, 17 % 0); // Infinity NaN
// 大きな整数は BigInt で割る。/ は小数部を切り捨てる
console.log(17n / 5n, -17n % 5n); // 3n -2nJavaScript の数値には整数型が無いので、/ で割った結果を Math.trunc で切り捨てて商にし、余りは % で求める。
% の余りは割られる数と同じ符号になり、-17 % 5 は -2 である。
0 で割っても例外にはならず、/ は Infinity、% は NaN を返すので、割る数が 0 でないことを先に確かめる。
BigInt どうしの / は小数部を切り捨てて商を返し、0n で割ったときは RangeError になる。
TypeScript 実行確認済み
// 商と余りを組で返す。0 で割ったときは Infinity や NaN を返さずに例外にする
function divmod(a: number, b: number): [quotient: number, remainder: number] {
if (!Number.isInteger(a) || !Number.isInteger(b)) {
throw new TypeError("整数を渡してください");
}
if (b === 0) {
throw new RangeError("0 では割れません");
}
return [Math.trunc(a / b), a % b];
}
const [q, r] = divmod(17, 5);
console.log(q, r); // 3 2
console.log(divmod(-17, 5)); // [ -3, -2 ]
try {
divmod(17, 0);
} catch (e) {
console.log(e instanceof RangeError ? e.message : e); // 0 では割れません
}divmod は商と余りを名前付きのタプル型で返すので、呼び出し側は分割代入でそれぞれを受け取れる。
0 で割ったときに Infinity や NaN が後の計算へ流れていかないよう、RangeError を投げて呼び出し側に知らせる。
number 型は小数も受け入れるため、Number.isInteger で整数かどうかを確かめてから割る。
Go 実行確認済み
package main
import "fmt"
func main() {
a, b := 17, 5
fmt.Println(a/b, a%b) // 3 2
// 商は 0 の側へ切り捨て、余りは割られる数と同じ符号になる
c := -17
fmt.Println(c/b, c%b) // -3 -2
// 0 以上の余りが欲しいときは、負なら割る数を足す
r := c % b
if r < 0 {
r += b
}
fmt.Println(r) // 3
fmt.Println(float64(a) / float64(b)) // 3.4
}整数どうしの / は商を 0 の側へ切り捨て、% の余りは割られる数と同じ符号になる。
0 以上の余りが欲しいときは、負になった余りに割る数を足して補正する。
変数の 0 で割ると実行時に panic: runtime error: integer divide by zero で止まり、a / 0 のように定数の 0 で割る式はコンパイル時に invalid operation: division by zero になる。
Rust 実行確認済み
fn main() {
let (a, b) = (17_i32, 5_i32);
println!("{} {}", a / b, a % b); // 3 2
// / と % は 0 の側へ切り捨てる。余りは割られる数と同じ符号になる
let c = -17_i32;
println!("{} {}", c / b, c % b); // -3 -2
// 割る数が正なら、div_euclid / rem_euclid で小さい側への商と 0 以上の余りが得られる
println!("{} {}", c.div_euclid(b), c.rem_euclid(b)); // -4 3
// 0 で割るとパニックになるので、入力由来の値は checked_div / checked_rem で受ける
println!("{:?} {:?}", a.checked_div(0), a.checked_rem(0)); // None None
println!("{:?}", a.checked_div(b)); // Some(3)
}/ と % は商を 0 の側へ切り捨て、余りは割られる数と同じ符号になる。
割る数が正のとき、div_euclid と rem_euclid は商を小さい側へ切り下げ、0 以上の余りを返す。
0 で割るとパニックするので、入力由来の割る数には Option を返す checked_div と checked_rem を使う。
-17_i32.div_euclid(5) と書くとメソッド呼び出しが先に結び付いて -3 になるため、負のリテラルは括弧で囲むか変数に入れる。
Java 実行確認済み
public class DivMod {
public static void main(String[] args) {
int a = 17, b = 5;
System.out.println(a / b + " " + a % b); // 3 2
// 商は 0 の側へ切り捨て、余りは割られる数と同じ符号になる
int c = -17;
System.out.println(c / b + " " + c % b); // -3 -2
// 商を小さい側へ切り下げ、余りを割る数と同じ符号にするなら floorDiv / floorMod
System.out.println(Math.floorDiv(c, b) + " " + Math.floorMod(c, b)); // -4 3
System.out.println(a / (double) b); // 3.4
try {
System.out.println(a / 0);
} catch (ArithmeticException e) {
System.out.println("ArithmeticException: " + e.getMessage()); // ArithmeticException: / by zero
}
}
}整数どうしの / は商を 0 の側へ切り捨て、% の余りは割られる数と同じ符号になる。
Python と同じく商を小さい側へ切り下げたいときは、Math.floorDiv と Math.floorMod を使う。
整数を 0 で割ると ArithmeticException が投げられるが、どちらかが double なら例外にはならず、17.0 / 0 は Infinity になる。
C# 実行確認済み
int a = 17, b = 5;
Console.WriteLine($"{a / b} {a % b}"); // 3 2
// Math.DivRem は商と余りをタプルで返す(.NET 6 以降)
var (q, r) = Math.DivRem(-17, b);
Console.WriteLine($"{q} {r}"); // -3 -2
// 0 以上の余りが欲しいときは、割る数を足してからもう一度 % を取る
Console.WriteLine((-17 % b + b) % b); // 3
Console.WriteLine((double)a / b); // 3.4
int zero = 0;
try
{
Console.WriteLine(a / zero);
}
catch (DivideByZeroException e)
{
Console.WriteLine(e.Message); // Attempted to divide by zero.
}整数どうしの / は商を 0 の側へ切り捨て、% の余りは割られる数と同じ符号になる。
Math.DivRem は .NET 6 以降で商と余りをタプルで返すので、分解して受け取れる。
0 以上の余りが欲しいときは、割る数を足してからもう一度 % を取る。
整数を 0 で割ると DivideByZeroException が投げられるが、double の割り算なら例外にはならず、17.0 / 0 は Infinity になる。
つまずき
負の数を割ったときの結果が言語で割れる。
-17 を 5 で割ると、Python の // と % は商 -4・余り 3 を返し、JavaScript・TypeScript・Go・Rust・Java・C# の / と % は商 -3・余り -2 を返す。
前者は商を小さい側へ切り下げ、後者は 0 の側へ切り捨てるためで、どちらでも「商 × 割る数 + 余り = 割られる数」は成り立っている。
配列の添字や曜日の計算のように 0 以上の余りを前提にしたコードを別の言語へ移すと、負の値が来たときだけ結果がずれる。
0 以上の余りの求め方
割る数が正のとき、0 以上の余りは Python ならそのまま %、Java なら Math.floorMod、Rust なら rem_euclid で得られる。
JavaScript・Go・C# では、((a % b) + b) % b のように割る数を足してからもう一度 % を取るか、負になった余りにだけ割る数を足す。
割る数が負になると、Python の 17 % -5 は -3、Java の Math.floorMod(-17, -5) は -2 のように余りが 0 以下になり、この 3 つのうち 0 以上を返したのは Rust の rem_euclid だけだった。
0 で割ったときの振る舞い
整数を 0 で割ったときの振る舞いも揃っていない。
Python は ZeroDivisionError、Java は ArithmeticException、C# は DivideByZeroException を投げ、Go と Rust はパニックで止まる。
JavaScript の number は例外を出さずに Infinity や NaN を返し、その値が後の計算へ静かに流れていく。
割る数が入力や設定値に由来するなら、割る前に 0 かどうかを確かめる。
JavaScript で切り捨てにビット演算を使わない
JavaScript では (a / b) | 0 で小数部を落とす書き方を見かけるが、| は値を 32 ビットの整数に変換してから計算するので、32 ビットに収まらない値では結果が壊れ、2 ** 31 | 0 は -2147483648 になる。
商は Math.trunc で求め、Number.MAX_SAFE_INTEGER を超える整数は BigInt で割る。