閉じる

Pythonの/と//の違い: 整数割り算を切り捨てる基本

整数の割り算は、Pythonでは2種類の演算子(/ と //)で振る舞いが変わります。

本記事では、整数同士の割り算で切り捨てたいときに使う演算子 // に焦点を当て、/ との違い、余りの求め方、負の数の扱い、型のルール、実用例、そして落とし穴まで順に解説します。

Pythonの/と//の違いを理解する

/ は小数を返す割り算

/ は常に「真の割り算」を行い、小数(float)を返します。

整数同士でも結果は float です。

Python
# / は常に小数(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 です。

整数として扱いたい場面では後述の // を検討します。

// は切り捨ての整数割り算

// は「床(フロア)方向への切り捨て」を行う割り算です。

正の数では数学的な「切り捨て」と一致します。

Python
# // は床方向への切り捨てを行います
print(7 // 3)         # 7 ÷ 3 を切り捨て
print(10 // 2)        # 割り切れても // は使えます
print(type(7 // 3))   # 結果の型を確認
実行結果
2
5
<class 'int'>

例 7/3 と 7//3 の結果

同じ分子分母でも、/ と // では結果も型も異なります。

Python
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 が便利です。

Python
# 余りの計算
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 / 32.3333333333333335
//床方向の割り算7 // 32
%余り7 % 31
divmod(商, 余り)のタプルdivmod(7, 3)(2, 1)

// のルールと結果の型

オペランドがintならintを返す

両方のオペランドが int のとき、結果は int になります。

大きな整数でも問題ありません。

Python
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 になります。

Python
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)

負の数は床方向に切り捨て

床方向とは「負の無限大側へ丸める」ことです。

負の数が絡むと、いわゆる小学校の「切り捨て」とは結果が異なることがあります。

Python
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方向」に切り捨てます。

// は「床方向」なので、負の値で差が出ます。

Python
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 を返す点に注意)。

Python
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 になります。

Python
print(1.0 // 0.1)  # 0.1の誤差が影響
実行結果
9.0

厳密な小数計算が必要な場合は decimal モジュールの使用を検討します。

// の実用例

ページ数を計算する

検索結果などをページ分割するとき、必要ページ数は (総件数 + 1ページの件数 - 1) // 1ページの件数 で求められます。

最終ページの件数は余りでわかります。

Python
# 例: 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個に均等配置した代表インデックス」を、// を使った整数演算だけで作れます。

Python
# 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 で計算できます。

Python
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

時分秒に分解する

総秒数を時・分・秒に分解するのは // と % の典型的な用途です。

Python
# 例: 総秒数を時分秒に分解
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の場合、/ も // も % も例外が発生します。

入力を検証するか、例外処理で備えます。

Python
a, b = 5, 0
try:
    print(a // b)
except ZeroDivisionError as e:
    print("ZeroDivisionError:", e)
実行結果
ZeroDivisionError: integer division or modulo by zero

負の数の結果が期待と違う

// は床方向のため、負の数では「0方向の切り捨て」と一致しません。

Python
print(-7 // 3)   # -3 (床方向)
print(7 // -3)   # -3 (床方向)
実行結果
-3
-3

負の値を含むロジックでは、「床方向」であることを前提に式を設計する必要があります。

結果の型に注意 intかfloatか

片方でも float が混ざると、// の結果は float です。

型の取り扱いに注意します。

Python
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)

切り上げや四捨五入は別の手法

// は床方向の「切り捨て」です。

切り上げや四捨五入には専用の関数(または式)を使います。

Python
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) です。

Python
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除算や丸め方向の違いといった落とし穴を理解し、目的に応じて //%divmodmath.floormath.ceilround を使い分けていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!