🔟「十进制计算」全景速通:从“浮点误差”到“金额精确”的一次看懂!

42 阅读1分钟

微信图片_20251014151033_10_20.jpg

① 一句话原理:浮点误差是“天生”的 🧬

二进制无法精确表示 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