Python 浮点数类型
最后修改日期:2025 年 4 月 1 日
Python 的 float 类型表示遵循 IEEE 754 标准的浮点数。本教程涵盖浮点数的表示、使用模式、精度限制以及用于精确计算的替代方案。
我们将探讨浮点数的二进制表示,解释为什么不可能实现精确的十进制表示,并用实际示例演示常见的陷阱。理解这些概念对于金融和科学计算至关重要。
浮点数基础
Python 浮点数是 64 位(双精度)浮点数。
# Float literals
a = 3.14
b = 1.23e-4 # Scientific notation
c = float("inf") # Infinity
d = float("nan") # Not a Number
# Type checking
import math
print(type(a)) # <class 'float'>
print(math.isinf(c)) # True
print(math.isnan(d)) # True
# Special values
print(float('inf') * 2) # inf
print(float('nan') + 5) # nan
Python 浮点数遵循 IEEE 754 双精度标准,使用 64 位:1 位符号位,11 位指数位和 52 位小数位。这提供了大约 15-17 位有效十进制数字的精度。
像无穷大 (inf) 和非数字 (nan) 这样的特殊值会根据 IEEE 规则进行处理。math 模块提供了用于安全浮点数操作和检查的函数。
浮点数表示
浮点数以二进制科学计数法存储。
import struct
def float_to_bits(f):
packed = struct.pack('!d', f)
return ''.join(f'{byte:08b}' for byte in packed)
# Show binary representation
print(float_to_bits(0.1)) # 0011111110111001100110011001100110011001100110011001100110011010
print(float_to_bits(0.5)) # 0011111111100000000000000000000000000000000000000000000000000000
二进制表示展示了浮点数如何在内部存储。这 64 位被划分为:
- 1 位符号位(0=正,1=负)
- 11 位指数位(偏移量为 1023)
- 52 位小数位(带有隐式的开头的 1)
例如,0.5 存储为指数 -1(偏移形式为 1022)和小数位 0,结果为 1.0 × 2⁻¹ = 0.5。
精度限制
许多十进制数无法在二进制浮点数中精确表示。
# Exact representation test
a = 0.1 + 0.1 + 0.1
b = 0.3
print(a == b) # False
print(f"{a:.20f}") # 0.30000000000000004441
print(f"{b:.20f}") # 0.29999999999999998890
# Smallest difference
import sys
epsilon = sys.float_info.epsilon
print(epsilon) # 2.220446049250313e-16
经典的 0.1 + 0.1 + 0.1 ≠ 0.3 示例演示了浮点数的精度问题。这是因为二进制中的 0.1 是一个重复的小数(就像十进制中的 1/3),必须被截断。
机器 epsilon(2⁻⁵² ≈ 2.22e-16)表示 1.0 和下一个可表示的浮点数之间的最小差值。这是浮点数精度的基本限制。
误差累积
重复运算会放大浮点数误差。
# Summing many small numbers
total = 0.0
for _ in range(1_000_000):
total += 0.000001
print(total) # 0.9999999999999177 (not 1.0)
# Comparing floats safely
def almost_equal(a, b, rel_tol=1e-9, abs_tol=0.0):
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
print(almost_equal(total, 1.0)) # True
当对许多小的浮点数求和时,由于精度有限,误差会累积。结果会偏离数学上正确的值。
解决方案是使用基于容差的比较(如 math.isclose()),而不是精确的相等检查。该示例展示了如何实现一个基本版本。
Decimal 替代方案
要进行精确的十进制算术,请使用 decimal 模块。
from decimal import Decimal, getcontext
# Exact decimal arithmetic
a = Decimal('0.1') + Decimal('0.1') + Decimal('0.1')
b = Decimal('0.3')
print(a == b) # True
# Precision control
getcontext().prec = 28 # 28 decimal places
result = Decimal(1) / Decimal(7)
print(result) # 0.1428571428571428571428571429
# Financial calculations
price = Decimal('19.99')
tax = Decimal('0.08')
total = price * (1 + tax)
print(total) # 21.5892
decimal 模块提供十进制浮点数算术,并具有用户可定义的精度。它非常适合需要精确十进制表示的金融应用。
Decimal 以十进制存储数字,避免了二进制转换问题。但是,它的速度比原生浮点数慢,并且需要显式的精度管理。
Fraction 类型
要进行精确的有理数算术,请使用 fractions。
from fractions import Fraction # Exact fractions a = Fraction(1, 3) # 1/3 b = Fraction(1, 2) # 1/2 print(a + b) # 5/6 # Float to Fraction c = Fraction.from_float(0.1) print(c) # 3602879701896397/36028797018963968 # Exact calculations x = Fraction(1, 10) sum_fractions = sum([x, x, x]) print(sum_fractions == Fraction(3, 10)) # True
Fraction 类型将数字表示为精确的分子/分母对。这避免了对有理数的浮点数舍入误差。
将浮点数转换为分数可以揭示其精确的二进制表示。在需要通过计算精确保留比率时,Fraction 类型非常有用。
浮点数内部
Python 浮点数实现细节。
import sys
import math
# Float attributes
print(sys.float_info)
"""
sys.float_info(
max=1.7976931348623157e+308,
min=2.2250738585072014e-308,
dig=15,
mant_dig=53,
...
)
"""
# Float components
def float_components(f):
mant, exp = math.frexp(f)
return mant, exp
m, e = float_components(3.14)
print(f"Mantissa: {m}, Exponent: {e}") # Mantissa: 0.785, Exponent: 2
sys.float_info 结构揭示了特定于平台的浮点数特性。关键值包括最大/最小可表示数字和尾数精度。
math.frexp 函数将浮点数分解为尾数和指数部分。这显示了数字是如何在科学计数法中规范化的。
何时使用浮点数
浮点数的恰当使用场景。
# Scientific computing
def quadratic(a, b, c):
discriminant = b**2 - 4*a*c
sqrt_discriminant = math.sqrt(discriminant)
return (-b + sqrt_discriminant)/(2*a), (-b - sqrt_discriminant)/(2*a)
# Physical simulations
def simulate_projectile(v0, angle, dt=0.01):
angle_rad = math.radians(angle)
vx = v0 * math.cos(angle_rad)
vy = v0 * math.sin(angle_rad)
# Simulation loop using floats
...
# Graphics programming
def normalize_vector(x, y, z):
length = math.sqrt(x**2 + y**2 + z**2)
return x/length, y/length, z/length
浮点数非常适合科学计算、物理模拟和图形,其中:
- 近似结果是可接受的
- 性能至关重要
- 需要大动态范围
- 有硬件加速可用
这些示例展示了典型的浮点数应用,其中微小误差不会对结果产生有意义的影响。
最佳实践
- 避免精确比较: 使用 math.isclose() 或容差范围
- 注意累积: 求和误差会随着运算次数的增加而增长
- 金钱使用 Decimal: 金融计算需要精确性
- 考虑 Fraction: 在处理有理数时
- 了解您的精度: sys.float_info 显示系统限制
资料来源
作者
我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。
列出所有 Python 教程。