把 0.1 累加 100 次,得到的是 9.99999999999998——不是计算错了,是存进去的那 0.1 本来就不是 0.1。
第二章讲完了整数怎么用二进制表示,这一章接着问:那小数呢?这一节先用一个实验把问题摆出来。
一、先把实验做一遍
把 0.1 累加 100 次,结果是 9.99999999999998——不是 10,也没错。 更意外的是:不用等到一百次,加到第三次就偏了。 0.1 + 0.1 + 0.1 得到的是 0.30000000000000004。 教材用这个实验开篇,因为它足够小、足够怪、又足够普遍。
二、别急着怪语言
这不是 Python 的毛病,也不是某台机器坏了。 C、Java、JavaScript、Excel——所有用二进制浮点的东西,行为都一样。 所以它既不是 bug,也不是精度调参能解决的事。 它是【这种表示法本身的性质】。
三、这个现象说明什么
两条线索: 一、十进制的 0.1 在二进制里写不完——它是个无限循环小数。 二、计算机只能存有限位——写不完的部分只能砍掉。 于是存进去的那个数,已经不是 0.1 了,而是 0.1 的近似值。 拿近似值做一百次加法,结果自然不会正好是 10——误差是算出来的,不是坏出来的。
四、接下来要回答的两个问题
第一个问题:二进制到底怎么表示小数?为什么 0.1 偏偏不行?——3.2 回答。 第二个问题:既然会错,为什么计算机还敢用它算卫星轨道?——3.3 到 3.5 回答。 这一章的最后两节,会给出【确认误差】和【绕开误差】的具体做法。
五、动手 Demo:0.1 累加 100 次,结果不是 10
这是本书最有名的一个实验。不光一百次,有时加到第三次就偏了。
s = 0.0
for _ in range(100):
s += 0.1
print(s, s == 10.0)
print(0.1 + 0.1 + 0.1)
实跑输出:
--------------------------------------------------------------------
0.1 累加 100 次,结果不是 10
--------------------------------------------------------------------
这是本书最有名的一个实验。**不光一百次,有时加到第三次就偏了。**
把 0.1 累加 100 次(双精度浮点):
结果 9.99999999999998
等于 10? False
差多少 -1.954e-14
再看误差是第几步冒头的:
累加几次 结果 等于 n/10 吗
--------------------------------------------------
1 0.1 是
2 0.2 是
3 0.30000000000000004 否
10 0.9999999999999999 否
100 9.99999999999998 否
头两次还看不出来,**加到第三次就露馅了**。
这不是 Python 的毛病——任何用二进制浮点的语言都一样。
实测结果:累加 100 次得 9.99999999999998,与 10 相差约 -1.95e-14。 累加三次就已经偏了(0.30000000000000004)——误差不是攒到一百次才出现的。
30 秒实验
- 在你的语言里跑一遍这个实验,看看结果和这里是否一样。
- 再试试 0.2 + 0.4 和 0.1 + 0.7,看哪几对能正好凑上。
常见误区
- 以为这是语言或库的 bug。所有二进制浮点都一样,这是表示法的性质。
- 以为【多留几位小数】就能修好。问题不在小数位数,在二进制写不完。
自测三问(点开对答案)
1. 0.1 累加 100 次得到什么? —— 9.99999999999998,不是 10,与 10 相差约 -1.95e-14。 2. 这个现象是 bug 吗? —— 不是。所有用二进制浮点的语言、软件都一样,是表示法本身的性质。 3. 误差为什么会累积? —— 存进去的 0.1 已是近似值,每一跳都带一点偏差,加一百次就偏一百次。- ☐ 下一节(3.2 如何用二进制表示小数)
相关笔记
- 如何用二进制表示小数 — 0.1 到底卡在哪儿
- 计算机计算出错的原因 — 位数有限带来的必然结果
- 二进制到底是什么 — 整数那部分的位权
- 🔵 计算机基础 · 索引 — 硬件底层的入门课
- ⚙️ 程序是怎样跑起来的 · 索引