ZetCode

Python 浮点数类型

最后修改日期:2025 年 4 月 1 日

Python 的 float 类型表示遵循 IEEE 754 标准的浮点数。本教程涵盖浮点数的表示、使用模式、精度限制以及用于精确计算的替代方案。

我们将探讨浮点数的二进制表示,解释为什么不可能实现精确的十进制表示,并用实际示例演示常见的陷阱。理解这些概念对于金融和科学计算至关重要。

浮点数基础

Python 浮点数是 64 位(双精度)浮点数。

float_basics.py
# 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 模块提供了用于安全浮点数操作和检查的函数。

浮点数表示

浮点数以二进制科学计数法存储。

float_representation.py
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 位被划分为:

例如,0.5 存储为指数 -1(偏移形式为 1022)和小数位 0,结果为 1.0 × 2⁻¹ = 0.5。

精度限制

许多十进制数无法在二进制浮点数中精确表示。

precision_issues.py
# 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 和下一个可表示的浮点数之间的最小差值。这是浮点数精度的基本限制。

误差累积

重复运算会放大浮点数误差。

error_accumulation.py
# 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 模块。

decimal_alternative.py
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

fraction_type.py
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 浮点数实现细节。

float_internals.py
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 函数将浮点数分解为尾数和指数部分。这显示了数字是如何在科学计数法中规范化的。

何时使用浮点数

浮点数的恰当使用场景。

float_usecases.py
# 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

浮点数非常适合科学计算、物理模拟和图形,其中:

这些示例展示了典型的浮点数应用,其中微小误差不会对结果产生有意义的影响。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 Python 教程