IEEE 754(英文全称:IEEE Standard for Floating-Point Arithmetic,中文:浮点数算术标准)由 IEEE(电气电子工程师学会)于 1985 年首次发布,是目前全球最广泛使用的浮点数计算标准。2008 年发布修订版(IEEE 754-2008,新增十进制浮点格式),2019 年再次更新(IEEE 754-2019)。
官方地址:https://standards.ieee.org/standard/754-2019.html
制定者:加州大学伯克利分校的 William Kahan 教授主导设计,他因此获得 1989 年图灵奖,被称为「浮点数之父」。
几乎所有现代计算机(包括 CPU、GPU)的浮点运算都遵循此标准。它定义了浮点数在内存中的二进制存储格式,是所有科学计算的底层基础。
一个浮点数被拆分为三部分:
(-1)^符号位 × 1.尾数 × 2^(指数 - 偏移量)
| 类型 | 总位数 | 符号位 | 指数位 | 尾数位 | 有效精度(十进制) |
|---|---|---|---|---|---|
| float16(半精度) | 16 | 1 | 5 | 10 | ~3 位 |
| float32(单精度) | 32 | 1 | 8 | 23 | ~7 位 |
| float64(双精度) | 64 | 1 | 11 | 52 | ~15 位 |
IEEE 754 用二进制表示小数,但很多十进制小数在二进制下是无限循环的:
0.1(十进制)= 0.00011001100110011...(二进制,无限循环)
0.2(十进制)= 0.00110011001100110...(二进制,无限循环)
就像十进制无法精确写出 1/3 = 0.3333...一样,二进制也无法精确表示 0.1。浮点数只能截断到有限位,产生微小误差。
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
原因链:0.1 和 0.2 各自存储时就有微小截断误差 → 两个近似值相加 → 误差叠加 → 结果不再是精确的 0.3。这不是 Python 的 bug,而是所有使用 IEEE 754 的语言(C/C++/Java/JS/Go……)的共同行为。
IEEE 754 是科学计算的双刃剑:
因此,理解 IEEE 754 是理解科学计算精度问题的起点。下文的各个 Python 数学库,本质上都是在 IEEE 754 的不同局限上提供补救方案。
Python 内置的浮点数学工具箱,底层是 C 的 double(float64),精度约 15~17 位有效数字。
import math
math.pi # 3.141592653589793
math.e # 2.718281828459045
math.sqrt(2) # 1.4142135623730951
math.log(10) # 2.302585092994046(自然对数)
math.sin(math.pi / 2) # 1.0
math.factorial(10) # 3628800
math.gcd(12, 8) # 4
适用场景:日常数学运算、几何计算、三角函数。 局限:受限于 IEEE 754 双精度,无法做任意精度计算。
math 的复数版本,支持复数的三角函数、对数、开方等。
import cmath
cmath.sqrt(-1) # 1j
cmath.exp(1j * cmath.pi) # (-1+1.2246467991473532e-16j) 即欧拉公式 e^(iπ) = -1
专为金融/会计场景设计,解决浮点数无法精确表示十进制小数的问题。
from decimal import Decimal, getcontext
# 经典问题:0.1 + 0.2 ≠ 0.3
0.1 + 0.2 # 0.30000000000000004(IEEE 754 浮点误差)
Decimal('0.1') + Decimal('0.2') # Decimal('0.3')(精确)
# 用 mpmath 验证误差来源
from mpmath import mp
mp.dps = 20
mp.mpf('0.1') # 0.1 在精确算术中的真值
# 而 float(0.1) 实际存储的是 0.1000000000000000055511151231257827021181583404541015625
为什么 0.1 + 0.2 会出错? 因为 0.1 在二进制下是无限循环小数(0.0001100110011...),IEEE 754 只能截断存储,0.2 同理。两个近似值相加,误差叠加,结果不再是精确的 0.3。decimal 通过十进制内部表示绕过了二进制截断问题。
# 控制运算精度
getcontext().prec = 50
Decimal(1) / Decimal(7) # 50 位有效数字
关键特性:
- prec 控制的是有效数字总位数(含整数部分),不是小数位数
- 从字符串创建 Decimal('3.14') 精确存储;从浮点数创建 Decimal(3.14) 会带入浮点误差
- 支持自定义舍入模式(ROUND_HALF_UP 等),适合货币计算
适用场景:金额计算、需要精确十进制表示的场景。
用分子/分母精确表示有理数,避免浮点近似。
from fractions import Fraction
Fraction(1, 3) + Fraction(1, 6) # Fraction(1, 2)
Fraction('0.1') + Fraction('0.2') # Fraction(3, 10)
float(Fraction(22, 7)) # 3.142857142857143
适用场景:需要精确有理数运算、教学演示。
内置的基础统计函数。
import statistics
data = [2.75, 1.75, 1.25, 0.75, 0.5, 1.25, 3.5]
statistics.mean(data) # 1.6785714285714286
statistics.median(data) # 1.25
statistics.stdev(data) # 1.035749322937853
statistics.variance(data) # 1.0727777777777778
import random
random.random() # [0.0, 1.0) 均匀分布
random.randint(1, 100) # [1, 100] 随机整数
random.gauss(0, 1) # 正态分布
random.choice([1, 2, 3]) # 随机选择
高精度数值计算的瑞士军刀,可计算数学常数和函数到任意精度。
from mpmath import mp
mp.dps = 100 # 小数点后 100 位(decimal places)
mp.pi # π 的 100 位精确值
mp.e # e 的 100 位精确值
mp.sqrt(2) # √2 的 100 位精确值
mp.sin(mp.pi / 4) # sin(π/4) 高精度结果
关键特性:
- dps(decimal places)控制小数点后的位数,语义比 decimal.prec 更直观
- 内置几十个数学常数(π、e、γ、φ……)和数百个数学函数
- SymPy 的底层数值引擎就是 mpmath
- 算法实时计算,非硬编码
适用场景:数学研究、验证浮点精度损失、计算常数到任意位数。
Python 科学计算生态的核心,提供高性能多维数组和向量化运算。
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.dot(a, b) # 32
np.mean(a) # 2.0
np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1. ]
np.random.randn(3, 3) # 3×3 标准正态随机矩阵
关键特性: - 底层 C/Fortran 实现,比纯 Python 循环快 100 倍以上 - 支持 float16/float32/float64/float128 等多种精度 - 广播机制(Broadcasting)简化多维运算 - 模型量化的核心工具:权重的截取、缩放、类型转换都通过 numpy 完成
适用场景:矩阵运算、信号处理、图像操作、机器学习数据预处理。
基于 numpy 构建,提供更高级的科学计算算法。
from scipy import optimize, integrate, signal
# 求方程根
optimize.fsolve(lambda x: x**3 - 2*x - 5, 2) # [2.09455148]
# 数值积分
integrate.quad(lambda x: x**2, 0, 1) # (0.33333333333333337, 3.7e-15)
# 信号滤波
signal.butter(4, 0.2) # 4 阶 Butterworth 滤波器
子模块覆盖:优化、积分、插值、统计、线性代数、信号处理、图像处理、稀疏矩阵等。
做代数推导而非数值计算,能输出精确的数学表达式。
from sympy import symbols, diff, integrate, solve, pi
x = symbols('x')
diff(x**3, x) # 3*x**2(求导)
integrate(x**2, x) # x**3/3(不定积分)
solve(x**2 - 2, x) # [-√2, √2](精确解,非近似值)
pi.evalf(100) # π 的 100 位(底层调用 mpmath)
适用场景:公式推导、方程求解、教学演示、LaTeX 输出。
C 库 GMP 的 Python 绑定,速度远超纯 Python 实现。
import gmpy2
gmpy2.mpfr('3.14', 200) # 200 位精度的浮点数
gmpy2.fac(100) # 100! 的精确值(158 位整数)
适用场景:需要极致性能的高精度计算(如密码学、大数分解)。
| 需求 | 推荐库 | 安装 |
|---|---|---|
| 日常数学 | math |
自带 |
| 金额/财务 | decimal |
自带 |
| 精确分数 | fractions |
自带 |
| 基础统计 | statistics |
自带 |
| 看 π 到 1000 位 | mpmath |
pip install mpmath |
| 矩阵/数组/量化 | numpy |
pip install numpy |
| 优化/积分/信号 | scipy |
pip install scipy |
| 符号推导/公式 | sympy |
pip install sympy |
| 极速大数运算 | gmpy2 |
pip install gmpy2 |
模型量化本质上是在不同数值精度之间做权衡:
理解这些数学库的行为,有助于深入理解量化过程中的精度损失机制。