📄 一句话概括
先分清场合——图形与信号可以带误差,钱和账号一位都不能错;要精确就换表示法:整数、Decimal、分数。
📝 本节和前面几节的关系
3.6 把误差看清楚了,这一节给出处理它的办法,也是本章的收口。
一、先分清:什么时候可以不管
可以带误差的场合:图形渲染、信号处理、科学计算、机器学习。 这些场合里,第 15 位的误差根本没人看得见,硬要消掉反而拖慢速度。 一位都不能错的场合:金额、账目、库存、序号、密码、坐标索引。 第一句话就该问自己:这个数字错了,会不会有人来找我? 会,就必须换表示法。
二、办法一:改用整数
最省事也最可靠的一招——把单位放大,改用整数。 金额以【分】为单位存:19.99 元存成 1999,加法减法全是整数运算,一位不差。 要显示的时候再除回来。除法只做一次,误差最多出现一次。 这也是各家支付系统在后台普遍的做法。
三、办法二、三:换一个更合适的表示法
Decimal:按十进制算,专为财务设计。0.1 加 100 次正好是 10。 分数(fractions):存 1/3 就真的存 1/3,不做除法就没有误差。 两者都比浮点慢、也比浮点占地方——这是【精确】的代价,付得值不值看场合。
四、办法四、五:改掉两个坏习惯
比较判等要带容差:不要写 a == b,写成 abs(a - b) < 1e-9。 能一次算完就别累加:求和尽量用公式或库函数,别一步一舍。 两条都极其便宜,改起来只是一行代码。 记住那句话就够了:要精确,就别用浮点;非用不可,就别判等、别累加。
五、动手 Demo:避开小数误差的几种办法
误差来自【能表示的位数有限】,所以办法都围绕【别拿浮点存精确值】。
from decimal import Decimal
s = 0.0
for _ in range(100):
s += 0.1
d = sum(Decimal("0.1") for _ in range(100))
print("浮点 ", s, s == 10.0)
print("整数 ", 100, 100 == 100)
print("Decimal", d, d == Decimal("10"))
实跑输出:
--------------------------------------------------------------------
避开小数误差的几种办法
--------------------------------------------------------------------
**误差来自【能表示的位数有限】,所以办法都围绕【别拿浮点存精确值】。**
办法 怎么做 适合什么场景
-----------------------------------------------------------
改用整数 金额以【分】为单位存 钱、计数、库存
用 Decimal decimal 模块按十进制算 财务、报表
用分数 fractions 模块保真 比例、精确推导
比较设容差 abs(a-b) < 1e-9 判定相等 任何判等
别做累加 能一次算完就不要循环加 统计求和
实跑三行对照:
浮点 0.1 加 100 次 = 9.99999999999998 等于 10? False
整数 1 加 100 次 = 100 等于 100? True
Decimal 0.1 加 100 次 = 10.0 等于 10? True
**一条原则:要精确,就别用浮点。**
浮点是为【大范围、可带误差】的量设计的——科学计算、图形、信号处理都合适;
钱、账、序号这类【一位都不能错】的东西,从一开始就该换表示法。
浮点累加 100 次得 9.99999999999998;整数与 Decimal 都精确得到结果。 一条原则:要精确,就别用浮点——钱、账、序号从一开始就该换表示法。
30 秒实验
- 把一段涉及金额的代码改成【以分为单位】的整数运算,看还有没有误差。
- 找出你项目里所有直接比较浮点数的语句,给它们加上容差。
常见误区
- 一律改成 Decimal【以防万一】。可带误差的场合用它只会白白变慢。
- 用四舍五入显示来掩盖误差。误差还在,只是被藏起来了。
自测三问(点开对答案)
1. 什么样的场合必须避免浮点误差? —— 金额、账目、库存、序号、密钥——错了会有人找上门的那类数据。 2. 处理金额最常用的办法是什么? —— 以最小单位(如【分】)存整数,显示时再换算回去。 3. 比较两个浮点数该怎么写? —— 不要直接判等,改成判断差值是否小于一个容差(如 abs(a-b) < 1e-9)。- ☐ 下一章(第四章 让内存化方为圆)
相关笔记
- 将0.1累加100次的结果不是10 — 本章开头那个实验
- 用程序来实际确认一下吧 — 先把误差看清楚,再谈绕开
- 🔵 计算机基础 · 索引 — 硬件底层的入门课
- ⚙️ 程序是怎样跑起来的 · 索引