整数の割り算は、Pythonでは2種類の演算子(/ と //)で振る舞いが変わります。
本記事では、整数同士の割り算で切り捨てたいときに使う演算子 // に焦点を当て、/ との違い、余りの求め方、負の数の扱い、型のルール、実用例、そして落とし穴まで順に解説します。
Pythonの/と//の違いを理解する
/ は小数を返す割り算
/ は常に「真の割り算」を行い、小数(float)を返します。
整数同士でも結果は float です。
# / は常に小数(float)を返します
a, b = 7, 3
print(a / b) # 7 ÷ 3 の小数結果
print(10 / 2) # 10 ÷ 2 も float
print(type(a / b)) # 結果の型を確認
2.3333333333333335
5.0
<class 'float'>
注意
見た目が整数のような 5.0 でも、型は float です。
整数として扱いたい場面では後述の // を検討します。
// は切り捨ての整数割り算
// は「床(フロア)方向への切り捨て」を行う割り算です。
正の数では数学的な「切り捨て」と一致します。
# // は床方向への切り捨てを行います
print(7 // 3) # 7 ÷ 3 を切り捨て
print(10 // 2) # 割り切れても // は使えます
print(type(7 // 3)) # 結果の型を確認
2
5
<class 'int'>
例 7/3 と 7//3 の結果
同じ分子分母でも、/ と // では結果も型も異なります。
a, b = 7, 3
print(f"{a}/{b} = {a / b}")
print(f"{a}//{b} = {a // b}")
print(f"type({a}/{b}) = {type(a / b).__name__}")
print(f"type({a}//{b}) = {type(a // b).__name__}")
7/3 = 2.3333333333333335
7//3 = 2
type(7/3) = float
type(7//3) = int
余りは % と divmod で求める
余りは % で求められます。
商と余りを同時に得たいときは divmod が便利です。
# 余りの計算
a, b = 7, 3
print(f"{a} % {b} = {a % b}")
# 商と余りを同時に取得
q, r = divmod(a, b)
print(f"divmod({a}, {b}) = (商{q}, 余り{r})")
# 負の数を含む場合の挙動
a, b = -7, 3
print(f"{a} % {b} = {a % b}")
print(f"divmod({a}, {b}) = {divmod(a, b)}")
7 % 3 = 1
divmod(7, 3) = (商2, 余り1)
-7 % 3 = 2
divmod(-7, 3) = (-3, 2)
余りの符号のルール
Pythonでは、余り r は「0 ≤ r < |b| かつ r は除数 b と同じ符号(または0)」になるように定義されています。
b が正なら余りは必ず 0 以上になります。
次の表はよく使う演算の違いを簡単にまとめたものです。
演算 | 役割 | 例(a=7, b=3) | 結果 |
---|---|---|---|
/ | 真の割り算(小数) | 7 / 3 | 2.3333333333333335 |
// | 床方向の割り算 | 7 // 3 | 2 |
% | 余り | 7 % 3 | 1 |
divmod | (商, 余り)のタプル | divmod(7, 3) | (2, 1) |
// のルールと結果の型
オペランドがintならintを返す
両方のオペランドが int のとき、結果は int になります。
大きな整数でも問題ありません。
pairs = [(7, 3), (10, 2), (123456789123456789, 10)]
for a, b in pairs:
q = a // b
print(f"{a} // {b} = {q} (type: {type(q).__name__})")
7 // 3 = 2 (type: int)
10 // 2 = 5 (type: int)
123456789123456789 // 10 = 12345678912345678 (type: int)
floatが含まれるとfloatで返す
どちらか一方でも float を含むと、結果は float になります。
tests = [(7.0, 3), (7, 3.0), (-7.0, 3.0)]
for a, b in tests:
q = a // b
print(f"{a} // {b} = {q} (type: {type(q).__name__})")
7.0 // 3 = 2.0 (type: float)
7 // 3.0 = 2.0 (type: float)
-7.0 // 3.0 = -3.0 (type: float)
負の数は床方向に切り捨て
床方向とは「負の無限大側へ丸める」ことです。
負の数が絡むと、いわゆる小学校の「切り捨て」とは結果が異なることがあります。
pairs = [(-7, 3), (7, -3), (-7, -3)]
for a, b in pairs:
print(f"{a} // {b} = {a // b}, 余り {a % b}")
-7 // 3 = -3, 余り 2
7 // -3 = -3, 余り -2
-7 // -3 = 2, 余り -1
ポイント
- -7 // 3 は「-2」ではなく「-3」です(床方向)。
- 余りは除数と同じ符号を取ります。
intで丸める方法との違い
int(x)
は小数点以下を「0方向」に切り捨てます。
// は「床方向」なので、負の値で差が出ます。
x, y = -7, 3
print(f"int({x}/{y}) = {int(x / y)}") # 0方向へ切り捨て
print(f"{x}//{y} = {x // y}") # 床方向へ切り捨て
int(-7/3) = -2
-7//3 = -3
math.floor と同じ丸め方向
// は本質的に math.floor(x / y)
と同じ丸め方向です(結果の型は // が int あるいは float を返す点に注意)。
import math
pairs = [(7, 3), (-7, 3), (7, -3), (-7, -3)]
for a, b in pairs:
print(f"{a}//{b} = {a // b}, floor({a}/{b}) = {math.floor(a / b)}")
7//3 = 2, floor(7/3) = 2
-7//3 = -3, floor(-7/3) = -3
7//-3 = -3, floor(7/-3) = -3
-7//-3 = 2, floor(-7/-3) = 2
浮動小数点の丸め誤差に注意
浮動小数点の表現誤差により、期待とわずかに異なる結果になることがあります。
例えば 0.1 は2進数で正確に表せないため、1.0 // 0.1 は 10.0 ではなく 9.0 になります。
print(1.0 // 0.1) # 0.1の誤差が影響
9.0
厳密な小数計算が必要な場合は decimal
モジュールの使用を検討します。
// の実用例
ページ数を計算する
検索結果などをページ分割するとき、必要ページ数は (総件数 + 1ページの件数 - 1) // 1ページの件数
で求められます。
最終ページの件数は余りでわかります。
# 例: 53件を10件/ページで表示
total_items = 53
per_page = 10
# 必要ページ数(正の整数同士の定番式)
pages = (total_items + per_page - 1) // per_page
# 商と余りで確認
q, r = divmod(total_items, per_page) # q=各ページ満杯の数, r=最終ページの件数
pages_via_divmod = q + (1 if r else 0)
print(f"ページ数: {pages} (divmod版: {pages_via_divmod})")
print(f"最終ページの件数: {r if r else per_page}")
ページ数: 6 (divmod版: 6)
最終ページの件数: 3
等間隔のインデックスを求める
浮動小数を使わず「0からn-1までをk個に均等配置した代表インデックス」を、// を使った整数演算だけで作れます。
# 0..(n-1) の範囲で k 個の等間隔な整数インデックスを作る
n = 20 # 要素数
k = 5 # 欲しいインデックス数
indices = [i * (n - 1) // (k - 1) for i in range(k)]
print(indices) # 端(0とn-1)を含めて均等に分布
[0, 4, 9, 14, 19]
区間番号(バケット)を求める
値がどの区間(幅 width)に属するかは i // width
で計算できます。
width = 5
for i in range(12):
bucket = i // width
print(f"i={i} -> bucket={bucket}")
i=0 -> bucket=0
i=1 -> bucket=0
i=2 -> bucket=0
i=3 -> bucket=0
i=4 -> bucket=0
i=5 -> bucket=1
i=6 -> bucket=1
i=7 -> bucket=1
i=8 -> bucket=1
i=9 -> bucket=1
i=10 -> bucket=2
i=11 -> bucket=2
時分秒に分解する
総秒数を時・分・秒に分解するのは // と % の典型的な用途です。
# 例: 総秒数を時分秒に分解
total_seconds = 7384
# 直接 // と % を使う
h = total_seconds // 3600
m = (total_seconds % 3600) // 60
s = total_seconds % 60
print(f"{h}時間{m}分{s}秒")
print(f"{h:02}:{m:02}:{s:02}")
# divmod を使って段階的に分解
h2, rem = divmod(total_seconds, 3600)
m2, s2 = divmod(rem, 60)
print(f"(divmod) {h2}時間{m2}分{s2}秒")
2時間3分4秒
02:03:04
(divmod) 2時間3分4秒
よくある落とし穴と対策
0で割るとZeroDivisionError
分母が0の場合、/ も // も % も例外が発生します。
入力を検証するか、例外処理で備えます。
a, b = 5, 0
try:
print(a // b)
except ZeroDivisionError as e:
print("ZeroDivisionError:", e)
ZeroDivisionError: integer division or modulo by zero
負の数の結果が期待と違う
// は床方向のため、負の数では「0方向の切り捨て」と一致しません。
print(-7 // 3) # -3 (床方向)
print(7 // -3) # -3 (床方向)
-3
-3
負の値を含むロジックでは、「床方向」であることを前提に式を設計する必要があります。
結果の型に注意 intかfloatか
片方でも float が混ざると、// の結果は float です。
型の取り扱いに注意します。
cases = [(7, 2), (7, 2.0), (7.0, 2.0)]
for a, b in cases:
q = a // b
print(f"{a} // {b} = {q} (type: {type(q).__name__})")
7 // 2 = 3 (type: int)
7 // 2.0 = 3.0 (type: float)
7.0 // 2.0 = 3.0 (type: float)
切り上げや四捨五入は別の手法
// は床方向の「切り捨て」です。
切り上げや四捨五入には専用の関数(または式)を使います。
import math
a, b = 7, 3
print(f"切り上げ: math.ceil({a}/{b}) = {math.ceil(a / b)}")
print(f"四捨五入: round({a}/{b}, 2) = {round(a / b, 2)}")
print(f"round(2.5) = {round(2.5)}") # バンカー丸め: 偶数に揃う
print(f"round(3.5) = {round(3.5)}")
切り上げ: math.ceil(7/3) = 3
四捨五入: round(7/3, 2) = 2.33
round(2.5) = 2
round(3.5) = 4
切り上げの整数式
正の整数同士なら (a + b - 1) // b
で「切り上げ除算」になります。
符号に依存しない一般形は -(-a // b)
です。
import math
def ceil_div_pos(a: int, b: int) -> int:
# a, b > 0 に限定
return (a + b - 1) // b
def ceil_div(a: int, b: int) -> int:
# b != 0 で符号に依存しない一般形
return -(-a // b)
cases = [(7, 3), (-7, 3), (7, -3), (-7, -3)]
for a, b in cases:
print(f"ceil_div({a}, {b}) = {ceil_div(a, b)} (math.ceil={math.ceil(a / b)})")
ceil_div(7, 3) = 3 (math.ceil=3)
ceil_div(-7, 3) = -2 (math.ceil=-2)
ceil_div(7, -3) = -2 (math.ceil=-2)
ceil_div(-7, -3) = 3 (math.ceil=3)
まとめ
/ は真の割り算で常に float を返し、// は床方向への切り捨てを行います。
整数同士の割り算で小数以下を不要とする場合は // を使うのが基本で、余りは % や divmod で求められます。
型はオペランドに float が含まれると float に変わる点、負の数では床方向になる点が重要です。
ページ分割、等間隔のインデックス生成、時分秒の分解など、// は日常的な処理で大いに役立ちます。
0除算や丸め方向の違いといった落とし穴を理解し、目的に応じて //
、%
、divmod
、math.floor
、math.ceil
、round
を使い分けていきましょう。