Pythonのmath.modf()で整数部と小数部を分ける|負の数・divmodとの違い・誤差の対処
Pythonで数値の整数部と小数部を一度に取り出すには、math.modf(x)を使います。戻り値は(小数部, 整数部)の順のタプルで、どちらもfloat型、符号は元の数と同じです。
この記事のコードはPython 3.12.15で実行し、実際の出力を載せています(NumPyの例のみPython 3.9.6・NumPy 2.0.2)。関数の仕様はPython公式ドキュメントのmathモジュール(2026年10月17日確認)に沿っています。
math.modf()の基本
math.modf()は引数を1つ受け取り、小数部と整数部を2つのfloatで返します。順番が「小数部が先」なので、受け取るときに逆にしないよう注意してください。
import math
print(math.modf(3.14))
frac, whole = math.modf(3.14)
print(whole, frac)
print(type(whole), type(frac))
実行結果:
(0.14000000000000012, 3.0)
3.0 0.14000000000000012
<class 'float'> <class 'float'>
小数部が0.14ではなく0.14000000000000012になるのは、3.14という値が2進数の浮動小数点数では正確に表せないためです。math.modf()の不具合ではありません。この誤差の理由は浮動小数点数とは?符号部・指数部・仮数部で詳しく説明しています。
戻り値の仕様(公式ドキュメントより)
| 項目 | 内容 |
|---|---|
| 戻り値 | (小数部, 整数部) のタプル |
| 型 | どちらもfloat(int を渡しても float で返る) |
| 符号 | どちらも元の数 x と同じ符号 |
| C言語との違い | Cの modf は整数部をポインタ引数で返すが、Pythonではタプルで両方を返す |
正の数・負の数・整数を渡したとき
import math
for x in [3.75, -3.75, 5, -0.5, 2.0]:
print(x, math.modf(x))
実行結果:
3.75 (0.75, 3.0)
-3.75 (-0.75, -3.0)
5 (0.0, 5.0)
-0.5 (-0.5, -0.0)
2.0 (0.0, 2.0)
ポイントは3つです。
-3.75は(-0.75, -3.0)になり、小数部も整数部もマイナスです。- int の
5を渡しても(0.0, 5.0)とfloatで返ります。 -0.5の整数部は-0.0(マイナスのゼロ)になります。-0.0 == 0.0はTrueなので比較では困りませんが、表示すると「-0.0」と出るので、画面に出すときはint()で変換しておくと安全です。
int()・math.floor()・%・divmod()との違い
整数部や小数部を取り出す方法はほかにもあります。正の数では結果が同じでも、負の数では答えが分かれます。
import math
x = -3.75
print("modf :", math.modf(x))
print("int :", int(x))
print("math.trunc:", math.trunc(x))
print("math.floor:", math.floor(x))
print("x % 1 :", x % 1)
print("divmod :", divmod(x, 1))
print("math.fmod :", math.fmod(x, 1))
実行結果:
modf : (-0.75, -3.0)
int : -3
math.trunc: -3
math.floor: -4
x % 1 : 0.25
divmod : (-4.0, 0.25)
math.fmod : -0.75
| 書き方 | -3.75 の結果 | 丸めの向き・特徴 |
|---|---|---|
math.modf(x) | (-0.75, -3.0) | 0に向かって切り捨て。小数部と整数部を同時に返す |
int(x)・math.trunc(x) | -3 | 0に向かって切り捨て。整数部だけをint型で返す |
math.floor(x) | -4 | 小さいほうへ切り捨て(-3.75以下の最大の整数) |
x % 1 | 0.25 | floor基準の余り。結果は常に0以上 |
divmod(x, 1) | (-4.0, 0.25) | floor基準の商と余り。順番は(整数部, 小数部) |
math.fmod(x, 1) | -0.75 | 元の数と同じ符号の余り(modfの小数部と同じ) |
「-3.75を-3と-0.75に分けたい」ならmath.modf()、「-4と0.25に分けたい」(たとえば時刻や角度を常に正の端数で扱いたい)ならdivmod(x, 1)を使う、と覚えておくと迷いません。divmod()と%の負の数での動きはPythonのdivmod()で負の数・小数を割るとどうなる?で詳しく比べています。
無限大・NaN・とても大きな数
import math
for x in [float("inf"), float("-inf"), float("nan"), pow(2.0, 53) + 0.5, 1e20]:
print(x, math.modf(x))
実行結果:
inf (0.0, inf)
-inf (-0.0, -inf)
nan (nan, nan)
9007199254740992.0 (0.0, 9007199254740992.0)
1e+20 (0.0, 1e+20)
無限大を渡すと小数部は0.0(マイナス無限大なら-0.0)、NaNは両方NaNになります。公式ドキュメントには、floatの精度はふつう53ビットなので、絶対値が2の52乗以上のfloatには小数部が存在しないという注意書きがあります。上の例でpow(2.0, 53) + 0.5の0.5が消えているのはこのためで、足した時点で0.5が表現できずに失われています。
小数部の誤差を避けたいとき
お金や測定値など、小数部を「見たとおりの値」で扱いたい場面では、floatの誤差が問題になります。
import math
from decimal import Decimal
print(math.modf(1.1))
print(math.modf(123.45))
frac, whole = math.modf(123.45)
print(round(frac, 2))
d = Decimal("123.45")
print(d % 1, d // 1)
実行結果:
(0.10000000000000009, 1.0)
(0.45000000000000284, 123.0)
0.45
0.45 123
対処は2通りです。
- 表示や比較のために丸める:
round(frac, 2)のように、必要な桁で丸めてから使います。 - 最初から10進数で計算する:
decimal.Decimalを文字列から作れば、d % 1とd // 1で誤差のない小数部と整数部が得られます(負の数ではDecimalの%と//は0に向かって切り捨てる点がfloatと違います)。
丸め方の違い(偶数丸めなど)はPythonで四捨五入する方法にまとめています。
活用例1:時間(小数)を「○時間○分」に変換する
勤怠や作業時間のデータで「1.5時間」のような小数の時間を表示用に変換する例です。小数部に60を掛けて分にします。
import math
def hours_to_hm(hours):
frac, whole = math.modf(hours)
minutes = round(frac * 60)
h = int(whole)
if minutes == 60:
h, minutes = h + 1, 0
return f"{h}時間{minutes}分"
for t in [1.5, 2.25, 7.9999, 0.1]:
print(t, "->", hours_to_hm(t))
実行結果:
1.5 -> 1時間30分
2.25 -> 2時間15分
7.9999 -> 8時間0分
0.1 -> 0時間6分
7.9999時間は分に直すと「7時間60分」になってしまうため、60分になったら時間に繰り上げる処理を入れています。端数を丸める処理を入れるときは、このような繰り上がりを忘れがちです。
活用例2:緯度・経度を「度・分・秒」に変換する
10進数の緯度経度を、地図でよく使う度分秒(60進数)に変換する例です。math.modf()を2回使い、度→分→秒と順に分解します。
import math
def to_dms(deg):
frac, d = math.modf(abs(deg))
frac, m = math.modf(frac * 60)
s = frac * 60
sign = "-" if deg < 0 else ""
return f"{sign}{int(d)}度{int(m)}分{s:.2f}秒"
print(to_dms(35.681236)) # 東京駅付近の緯度
print(to_dms(139.767125)) # 東京駅付近の経度
実行結果:
35度40分52.45秒
139度46分1.65秒
負の緯度経度(南緯・西経)でも正しく分解できるよう、絶対値で計算してから符号を付けています。math.modf()は符号を残すので、そのまま使うと分や秒までマイナスになるためです。
活用例3:整数かどうかを判定する
import math
def is_whole(x):
return math.modf(x)[0] == 0.0
print(is_whole(4.0), is_whole(4.5))
print((4.0).is_integer(), (4.5).is_integer())
実行結果:
True False
True False
小数部が0かどうかで整数判定もできますが、floatならis_integer()メソッドのほうが意図が伝わりやすい書き方です。整数判定の方法はPythonで整数か判定する方法にまとめています。
NumPy配列ならnp.modf()
配列の要素をまとめて分解するときは、NumPyのnp.modf()を使います。NumPyの公式ドキュメント(2026年10月17日確認)のとおり、要素ごとに小数部と整数部を返し、負の数では両方が負になります。
import numpy as np
a = np.array([3.75, -3.75, 0.5, 10.0])
frac, whole = np.modf(a)
print(frac)
print(whole)
実行結果:
[ 0.75 -0.75 0.5 0. ]
[ 3. -3. 0. 10.]
戻り値の順番はmath.modf()と同じく「小数部、整数部」です。mathモジュールのほかの関数はPythonのmath関数一覧で確認できます。
よくある質問
Q. math.modf()の戻り値の順番は?
(小数部, 整数部) の順のタプルです。frac, whole = math.modf(x) のように小数部を先に受け取ります。divmod(x, 1) は (整数部, 小数部) の順なので逆です。
Q. math.modf()で整数部をint型で取得できますか?
できません。整数部もfloatで返るため、int型が必要なら int(whole) のように変換します。整数部だけが欲しいなら int(x) や math.trunc(x) でも同じ結果になります。
Q. 負の数を渡すとどうなりますか?
小数部も整数部も元の数と同じ符号になります。math.modf(-3.75) は (-0.75, -3.0) です。-4 と 0.25 に分けたい場合は divmod(x, 1) を使います。
Q. 小数部が0.14000000000000012のようにずれるのはなぜですか?
3.14のような値が2進数の浮動小数点数で正確に表せないためで、math.modf()の不具合ではありません。round()で丸めるか、decimal.Decimalで計算すると避けられます。
Q. NumPy配列に使えますか?
配列には np.modf() を使います。要素ごとに小数部と整数部の配列を返し、順番は math.modf() と同じです。
あわせて読みたい
学習の順序はPython学習のロードマップ、標準で使える機能の一覧はPython標準ライブラリのモジュール一覧にまとめています。
Pythonの関数・メソッドの全体像はPython基本関数・メソッド完全一覧にまとめています。







