① 一句话原理:浮点误差是“天生”的 🧬
二进制无法精确表示 0.1、0.2 → 近似存储 → 累加放大误差。
类比:用 1/3 表示 0.3,永远写不完。
>>> 0.1 + 0.2
0.30000000000000004 # 经典名场面
② 十进制武器:3 种解法一次记牢 📝
| 武器 | 精度 | 场景 | 示例 |
|---|---|---|---|
int | 无限 | 分/厘计算 | price = 12345 # 123.45 元 |
decimal.Decimal | 用户指定 | 金额/汇率 | Decimal('0.1') |
fractions.Fraction | 有理数 | 科学计算 | Fraction(1, 10) |
口诀:“金额用 Decimal,分厘用 int,分数用 Fraction”
③ Decimal 速通:5 行上手 ⚡️
from decimal import Decimal, getcontext
getcontext().prec = 6 # 全局精度 6 位
a = Decimal('0.1')
b = Decimal('0.2')
print(a + b) # 0.3 ← 精确!
print(a * 100) # 10.0 ← 无误差!
结论:字符串构造 → 无二进制误差!
④ 金额实战:分 → 元 → 格式化 💰
def to_yuan(fen: int) -> Decimal:
return Decimal(fen) / 100
def fmt_money(yuan: Decimal) -> str:
return f"¥{yuan:.2f}"
print(fmt_money(to_yuan(12345))) # ¥123.45
结论:“分转元,Decimal 除,格式化两位”
⑤ 汇率实战:交叉汇率 + 舍入 🌍
from decimal import ROUND_HALF_UP
usd_cny = Decimal('7.1234') # 1 USD = 7.1234 CNY
cny_jpy = Decimal('20.1234') # 1 CNY = 20.1234 JPY
# USD → JPY(交叉汇率)
usd_jpy = usd_cny * cny_jpy
print(usd_jpy.quantize(Decimal('0.0001'), ROUND_HALF_UP)) # 143.3655
结论:
quantize控制小数位,避免银行家舍入误差
⑥ 性能对比:Decimal vs float vs int ⚡️
import timeit, decimal
# 0.1 + 0.2 百万次
t_float = timeit.timeit('0.1 + 0.2', number=100_000) # ~0.01 s
t_decimal = timeit.timeit("Decimal('0.1') + Decimal('0.2')",
setup='from decimal import Decimal',
number=100_000) # ~0.1 s
t_int = timeit.timeit('123 + 456', number=100_000) # ~0.008 s
结论:int 最快,Decimal 慢 10 倍,float 最快但误差
⑦ 万能解毒剂:检查清单 ✅
| 场景 | 解毒法 |
|---|---|
| 金额计算 | Decimal('0.01') 或 int 分厘 |
| 汇率换算 | quantize 控制小数位 |
| 显示格式化 | f"{val:.2f}" |
| 性能敏感 | 用 int 分厘 + 最后转 Decimal |