Pythonの数値型は大きく整数のintと小数のfloatに分かれます。
両者は見た目が似ていても内部表現や得意分野が異なるため、正しく使い分けることが重要です。
本稿では、基礎から計算方法、型変換、精度の落とし穴、表示フォーマットや実用Tipsまで、実行例を交えて丁寧に解説します。
Pythonの整数intと小数floatの基礎
intの特徴:任意精度整数と典型的な用途
Pythonのint
は任意精度整数です。
桁数に理論上の上限はなく、メモリが許す限りどれだけ大きな整数でも扱えます。
個数やインデックス、ループ回数、離散的なカウントなど、誤差が許されない場面に適します。
# 任意精度整数の例:非常に大きな数の桁数を確認
n = 10 ** 100 # 1の後ろに0が100個
print("桁数:", len(str(n)))
print("末尾10桁:", str(n)[-10:]) # 表示確認のため末尾だけを見る
桁数: 101
末尾10桁: 0000000000
典型用途の例として、組み合わせや階乗、ハッシュ計算などで大きな整数が自然に現れます。
int
はオーバーフローしない一方、非常に大きくなると計算コストやメモリ使用量は増大します(後述)。
floatの特徴:倍精度浮動小数点(IEEE 754)と近似表現
Pythonのfloat
は通常、C言語のdouble
に相当し、IEEE 754の倍精度浮動小数点で実装されています。
有限のビット長で連続量を近似するため、ほとんどの小数は「ぴったり」ではなく「近い値」で表現されます。
# 浮動小数点の分解能(機械イプシロン)を確認
import sys
print(sys.float_info.epsilon) # 1.0に足しても1.0と区別できる最小の値
2.220446049250313e-16
この近似性が、0.1+0.2が厳密には0.3と等しくならないといった誤差の源になります(後述)。
Pythonでの型確認・作成方法とリテラル(10, 1_000, 1.5, 1e-3 など)
type()
やisinstance()
で型を確認できます。
数値リテラルは読みやすさのための桁区切り(_
)や指数表記(e
/E
)が使えます。
a = 10 # int
b = 1_000 # int(桁区切り)
c = 1.5 # float(小数点)
d = 1e-3 # float(指数表記、0.001)
print(type(a), type(b), type(c), type(d))
# 進数リテラルや文字列からの生成
e = 0b1010 # 2進数 -> 10
f = 0o12 # 8進数 -> 10
g = 0xA # 16進数 -> 10
h = int("FF", 16) # 文字列16進をintに
print(e, f, g, h)
<class 'int'> <class 'int'> <class 'float'> <class 'float'>
10 10 10 255
intとfloatの違い・使い分けの基準
使い分けの判断基準:個数・離散値はint、測定値・連続量はfloat
- 個数やインデックス、ID、ループ回数、ビット演算など、離散的で誤差が許されない値は
int
にします。 - 長さ・時間・質量・緯度経度など連続量や測定値は
float
が自然です(ただし誤差を意識します)。
日付や時刻はdatetime
、IDは文字列の方が適切な場合もあります。
数値で表せるからといって常にint
やfloat
にするのではなく、意味的な型を選ぶのが安全です。
金額など正確な小数はDecimal、比率はFractionの検討
金額や税率など、桁や丸め規則を厳密に守る必要があるときはdecimal.Decimal
が適しています。
有理数の比率計算はfractions.Fraction
で誤差なく扱えます(詳細は後述)。
from decimal import Decimal
from fractions import Fraction
price = Decimal("19.99")
tax_rate = Decimal("0.1") # 10%
ratio = Fraction(2, 3)
print(price * 3) # 正確
print(ratio + Fraction(1, 6)) # 2/3 + 1/6 = 5/6
59.97
5/6
パフォーマンス・メモリの観点と大きなintの計算コスト
int
は値が大きくなるほど内部の「桁」(ワード)数が増え、加減算・乗算・除算のコストやメモリ使用量が増えます。
一方でfloat
は常に固定サイズです(CPythonでは通常8バイトのC doubleに相当します)。
以下はメモリ消費の一例です(実際の数値は環境により異なります)。
import sys
small_int = 0
big_int = 10 ** 1000
flt = 0.0
print("small int:", sys.getsizeof(small_int))
print("big int :", sys.getsizeof(big_int))
print("float :", sys.getsizeof(flt))
small int: 28
big int : 160
float : 24
大きなint
のべき乗や大きな配列での保持は時間・メモリの面で重くなるため、アルゴリズムやデータ型の選択に注意が必要です。
計算方法の基本:演算子と挙動の違い
加減乗除・べき乗:+ – * / ** の結果型と典型パターン
+ - *
は、両辺がint
ならint
、どちらかがfloat
ならfloat
になります。/
は常に真の除算でfloat
を返します(両辺がint
でもfloat
)。**
は整数同士ならint
(巨大になる可能性)、float
が含まれればfloat
です。pow(a, b, mod)
の3引数形式は高速な整数の冪余を返します。
print(2 + 3, type(2 + 3))
print(2 * 3.0, type(2 * 3.0))
print(5 / 2, type(5 / 2)) # float
print(2 ** 10, type(2 ** 10)) # int
print(pow(2, 10, 1000)) # 1024 mod 1000
5 <class 'int'>
6.0 <class 'float'>
2.5 <class 'float'>
1024 <class 'int'>
24
/ と // と % の違い:真の除算、切り捨て除算、剰余(負数の挙動に注意)
/
は真の除算(浮動小数点)。//
は床関数に基づく切り捨て除算(負無限大方向への丸め)。%
は剰余で、常にa == (a // b) * b + (a % b)
を満たし、剰余の符号は除数b
に一致します。
print(7 / 3, 7 // 3, 7 % 3) # 2.333..., 2, 1
print(-7 / 3, -7 // 3, -7 % 3) # -2.333..., -3, 2
print(7 / -3, 7 // -3, 7 % -3) # -2.333..., -3, -2
2.3333333333333335 2 1
-2.3333333333333335 -3 2
-2.3333333333333335 -3 -2
丸めと小数点操作:round、math.floor、math.ceil、math.trunc の使い分け
round(x, ndigits)
は銀行家の丸め(偶数丸め)。ndigits
省略時は最も近い整数に丸めてint
を返します。math.floor(x)
は床(小さい方向へ切り捨て)、math.ceil(x)
は天井(大きい方向へ切り上げ)。math.trunc(x)
はゼロ方向への切り捨て。
import math
print(round(2.5), round(3.5)) # 偶数丸め -> 2, 4
print(round(2.675, 2)) # 二進表現の影響で 2.67
print(math.floor(-2.3), math.ceil(-2.3)) # -3, -2
print(math.trunc(-2.9)) # -2(ゼロ方向)
2 4
2.67
-3 -2
-2
型変換と比較のコツ【型変換と精度の注意点】
型変換:int()・float()・str()・文字列からの数値化(小数点の扱い)
int()
は整数文字列を、float()
は小数も含めて変換します。
int("10")
は成功しますが、int("10.0")
はエラーです(小数点は不可)。
def safe_int(s: str):
try:
return int(s)
except ValueError as e:
return f"ValueError: {e}"
print(int(3.9)) # 小数からのintはゼロ方向切り捨て -> 3
print(float("1e-3")) # 0.001
print(safe_int("10"))
print(safe_int("10.0")) # エラー例
print(str(123), str(3.14)) # 数値 -> 文字列
3
0.001
10
ValueError: invalid literal for int() with base 10: '10.0'
123 3.14
進数指定のint("FF", 16)
のように基数も指定できます。
float("nan")
やfloat("inf")
も作れます(後述の注意点参照)。
浮動小数点の安全な比較:math.isclose と相対誤差・絶対誤差
float
の直接比較は誤差のため危険です。
math.isclose(a, b, rel_tol, abs_tol)
で相対誤差と絶対誤差の許容範囲を指定して比較します。
import math
a = 0.1 + 0.2
print(a == 0.3) # しばしばFalse
print(math.isclose(a, 0.3, rel_tol=1e-9, abs_tol=0.0)) # 許容誤差内でTrue
False
True
大きさがゼロ付近の値を比較する場合はabs_tol
を適切に設定することが重要です。
特殊値の扱い:NaN・Infinity の検出と比較の落とし穴
NaN
(非数)はどの値とも等しくありません(自分自身とも)。
math.isnan
やmath.isinf
、math.isfinite
で判定します。
import math
x = float("nan")
y = float("inf")
z = -float("inf")
print(x == x, math.isnan(x)) # NaNは自分とも等しくない
print(y > 1e308, math.isinf(y)) # 無限大
print(math.isfinite(1.23), math.isfinite(y))
False True
True True
True False
NaN
を含む計算は結果がNaN
に伝播しやすいため、入力データ検証で早めに除去・置換しましょう。
精度と誤差の注意点【floatの落とし穴】
0.1 + 0.2 ≠ 0.3 の理由:二進小数と丸め誤差
10進で有限桁の0.1は二進では循環小数となり、有限ビットでは表現しきれません。
最も近い近似値で表現するため誤差が出ます。
s = 0.1 + 0.2
print("repr:", repr(s))
print("17桁表示:", format(s, ".17f"))
print("0.3の17桁:", format(0.3, ".17f"))
repr: 0.30000000000000004
17桁表示: 0.30000000000000004
0.3の17桁: 0.29999999999999999
等価性比較ではなく、前述のmath.isclose
などを用いるのが安全です。
金額計算はDecimal:コンテキスト、量子化(quantize)、丸めモード
decimal.Decimal
は10進演算で誤差の管理が容易です。
getcontext()
で精度や丸めモードを設定し、quantize
で桁数を揃えます。
from decimal import Decimal, getcontext, ROUND_HALF_UP, ROUND_FLOOR
getcontext().prec = 28 # 演算精度(有効桁数)
price = Decimal("19.99")
qty = Decimal("3")
subtotal = price * qty # 正確に59.97
tax_rate = Decimal("0.10")
tax = (subtotal * tax_rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
total = (subtotal + tax).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print("小計:", subtotal)
print("税額:", tax)
print("合計:", total)
# 端数処理の違い(切り捨て)
v = Decimal("1.005")
print("四捨五入:", v.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
print("切り捨て :", v.quantize(Decimal("0.01"), rounding=ROUND_FLOOR))
小計: 59.97
税額: 6.00
合計: 65.97
四捨五入: 1.01
切り捨て : 1.00
通貨単位ごとの規則(例:JPYは小数なし)に合わせて量子化の桁を変えます。
分数の厳密計算はFraction:比率・有理数の活用
fractions.Fraction
は有理数を既約分数として正確に扱います。
文字列から生成すれば10進小数も正確に有理数化できます。
from fractions import Fraction
print(Fraction(1, 3) * 6) # 2
print(Fraction("0.1") + Fraction("0.2") == Fraction("0.3")) # True(正確)
print(Fraction(0.1)) # 浮動小数点からは近似分数
print(Fraction(0.1).limit_denominator()) # 近い分数に制限
2
True
3602879701896397/36028797018963968
1/10
Fraction(0.1)
は二進表現由来の巨大分母になります。
必要に応じてFraction("0.1")
やlimit_denominator()
を使います。
表示フォーマットと実用Tips
表示の整形:f-string/format で小数点桁数・千位区切り・指数表記
f
文字列やstr.format
のフォーマット指定で見やすく表示できます。
n = 1234567
x = 1234.56789
print(f"{n:,}") # 千位区切り
print(f"{x:.2f}") # 小数2桁
print(f"{x:,.3f}") # 千位区切り + 小数3桁
print(f"{x:.2e}") # 指数表記
print(f"{0.125:.3%}") # パーセント
1,234,567
1234.57
1,234.568
1.23e+03
12.500%
金額はDecimal
のままフォーマットするのが安全です。
桁あふれや丸めのタイミングに注意します。
科学技術計算のfloat精度とNumPyでのdtype(float32/float64)
数値計算ライブラリNumPyではdtype
で精度を選べます。
float64
が標準で、float32
はメモリを節約できますが精度は劣ります。
pip install numpy
import numpy as np
a32 = np.array([0.1, 0.2, 0.3], dtype=np.float32)
a64 = np.array([0.1, 0.2, 0.3], dtype=np.float64)
print(a32.dtype, a64.dtype)
print(a32.sum(), a64.sum()) # 近いが等しくないことがある
print(a32.sum() == np.float32(0.6), np.isclose(a32.sum(), np.float32(0.6)))
print(a64.sum() == np.float64(0.6), np.isclose(a64.sum(), np.float64(0.6)))
float32 float64
0.6000001 0.6
False True
False True
シミュレーションや学習ではfloat32
で十分なこともありますが、感度の高い統計量や安定性重視の線形代数ではfloat64
を選ぶのが無難です。
比較はnp.isclose
/np.allclose
を使います。
よくある落とし穴まとめ:整数除算の結果型、負の剰余、intはオーバーフローしないが遅くなる
5 / 2
は2.5
(float
)になります。整数結果が欲しければ//
やdivmod
を使います。- 負数の
//
は床方向に丸めるため、-7 // 3 == -3
です。%
の符号は除数と同じになります。 int
はオーバーフローしない代わりに大きくなるほど遅くなります。桁数が巨大になる計算はアルゴリズムや表現(モジュラ演算、対数領域など)を検討しましょう。
まとめ
Pythonのint
は誤差なく離散値を扱える任意精度整数、float
は連続量を高速に近似できる倍精度浮動小数点です。
用途に応じて適切に使い分け、計算では/
と//
、丸め関数の違い、負数の剰余挙動を正しく理解することが重要です。
float
の比較はmath.isclose
を用い、金額はDecimal
、比率はFraction
の活用を検討してください。
表示はフォーマット指定で見やすく整え、科学技術計算ではNumPyのdtype
を目的に合わせて選択します。
型と丸め・誤差の基礎を押さえれば、数値計算の信頼性と保守性は大きく向上します。