<!-- title: 浮点数神课：小数为什么算不准 -->
<!-- domain: programming -->

# 浮点数神课：小数为什么算不准

编程世界有一个流传最广的“灵异事件”：在浏览器控制台里敲下 `0.1 + 0.2`，回车，屏幕上端端正正打出 `0.30000000000000004`。没有报错，没有警告，就是差了那么小数点后第十七位的万万分之几。所有主流语言——JavaScript、Python、C++、Java——在这个问题上整齐划一地“翻车”。有人第一次看到时以为自己电脑坏了，有人怀疑是编译器出了 bug，甚至有人认为这是“计算机不会算数”的铁证。

英国诺丁汉大学背景的老牌科普频道 **Computerphile** 的经典基础课 **《Floating Point Numbers》（浮点数）**，就是专门来回答这个问题的。这支时长十来分钟的视频在程序员社区常年被反复推荐，是“计算机如何存数字”这个话题下流传最广的入门课之一（播放数据以频道页面显示为准）。它用一黑板的手写推导讲透一件事：**这不是 bug，而是一份七十多年前就签好的、极其精明的工程契约**。看懂它，你不仅知道 0.1 加 0.2 为什么不等于 0.3，还能看懂这个数字时代所有关于“精度”的暗礁——从导弹拦截失败、火箭爆炸，到今天 AI 芯片为什么抢着用更“粗”的数字格式。

## 视频基本信息

| 项目 | 内容 |
|------|------|
| 视频标题 | Floating Point Numbers（浮点数） |
| 频道 | Computerphile（诺丁汉大学学者出镜的计算机科普频道） |
| 时长 | 约 10 分钟（以频道页面显示为准） |
| 发布时间 | 2010 年代中期上线，常年位列频道基础课推荐榜（以频道页面显示为准） |
| 播放量 | 百万级常青内容（以频道页面显示为准） |
| 核心主题 | IEEE 754 浮点标准：计算机为什么“算不准”小数 |

## 核心主张拆解

### 一、先摊牌：所有语言一起翻车的一道算术题

视频的开场就是那句灵魂拷问：0.1 加 0.2，为什么等于 0.30000000000000004？出镜讲师在纸上把这个数算给你看——0.1 在二进制下不是“一位小数”，而是一个**无限循环小数**：0.0001100110011001100…，"0011"这组数字永远循环下去。

这跟十进制里 1/3 = 0.333…是同一类病：某个数在你选的进制下除不尽。十进制里 1/3 写不完，是因为 3 不整除 10 的幂；而 0.1 写不完，是因为 10 的因子（2 和 5）里只有 2 能整除 2 的幂，5 不行。**凡是分母含有 2 和 5 以外因子的十进制小数，在二进制下都是无限循环**。而计算机的内存是有限的，无限循环数只能截断——误差从这一刻就注定了。

关键认知在这里发生了第一次反转：**0.1 + 0.2 出错，不是“计算机算错了加法”，而是“你输入的两个数从一开始就没被精确存进去”**。加法本身算得一点没错，它只是忠实地把两个近似值加起来，得到了一个近似的结果。

### 二、浮点的“浮”字：二进制版的科学计数法

接下来视频进入正题：既然内存有限，数字该怎么存？答案就是每个程序员都听过、但很少真正拆开看过的 **IEEE 754 标准**。

它的思路和科学计数法一模一样：把一个数拆成三段——**符号位**（正负）、**指数**（阶码）、**尾数**（有效数字）。所谓“浮点”，浮的就是小数点：指数变一变，小数点就在二进制位里左右滑动，所以叫“浮动的小数点”。常用两种规格：

- **单精度（float32）**：32 位 = 1 符号 + 8 指数 + 23 尾数，约合十进制 7 位有效数字；
- **双精度（float64 / double）**：64 位 = 1 符号 + 11 指数 + 52 尾数，约合十进制 15~16 位有效数字。

视频里最漂亮的一个细节是**隐藏位技巧**：二进制的科学计数法中，首位有效数字必然是 1（就像十进制科学计数法首位必然是 1~9），既然永远是 1，就不用存了——白赚一位精度。这 53 位（52 存储 + 1 隐藏）就是双精度的全部家当。

而为什么 0.1 + 0.2 恰好差“那么一点点”？因为双精度约合 16 位十进制精度，0.1 和 0.2 各自被舍入到第 17 位附近才对齐，误差累积后离 0.3 差了 0.00000000000000004——恰好卡在“能被打印出来”的边界上。这是精度悬崖的第一次露脸。

### 三、数轴是稀疏的：数字越大，缝隙越大

视频后半段讲了一个大多数入门材料都略过、但极其重要的图景：**浮点数铺出来的数轴不是连续的，而是一把越往右齿距越大的尺子**。

在 1.0 附近，两个相邻浮点数的间隔约为 0.0000000000000002（2 的负 52 次方量级），细得像发丝；但在 10 的 20 次方附近，相邻两个浮点数之间已经隔着**一万六千多**——比很多国家的 GDP 与你钱包的差距还大。数字每翻一倍，网格的缝隙也翻一倍。

这直接解释了几个著名怪象。第一，**JavaScript 的整数悬崖**：JS 的 Number 用双精度存所有数字，当整数超过 2 的 53 次方（约 9007 万亿），相邻可表示整数间距变成 2——`9007199254740992 + 1` 的结果还是 `9007199254740992`。这就是 JS 只有“安全整数”概念、2020 年才补上 BigInt 类型的原因。第二，**游戏世界的坐标抖动**：开放世界游戏里，角色离原点几万米后模型开始抽搐，因为坐标的浮点缝隙大到比人物鼻子还宽，引擎被迫改用“相机相对坐标”这类技巧来绕开它。第三，**加法失去结合律**：`(1e20 - 1e20) + 1` 等于 1，但 `1e20 + (-1e20 + 1)` 等于 0——括号位置不同，天差地别，因为 1 落进 1e20 的缝隙里，被直接吞掉了。

### 四、真正的主角不是“错误”，而是“契约”

如果视频只讲到“计算机存不准小数”，它就只是又一条猎奇科普。它真正的价值在收尾处点破：浮点数不是随机的、不可理喻的错误堆，而是一份**每个步骤都被严格定义的标准**。

IEEE 754 对加减乘除和开方都做出承诺：**每一步运算结果都是“正确舍入”的**——即真实数学结果四舍五入到最近的浮点数，误差不超过半个最小格。它还统一规定了无穷大、NaN（非数）、次正规数（用来实现“渐进下溢”，让极小数字平滑趋零而非突然归零）的行为。NaN 甚至有个反直觉但极其实用的性质：它不等于任何数，包括它自己——所以 `x !== x` 是检测 NaN 的标准手法。

换句话说：**浮点的每一小步都是确定的、跨平台一致的、误差有严格上界的**。不精确，但可预算、可复现、可分析。这份契约，才是 1985 年那个标准文件真正的历史功绩。

## 知识与理论溯源

视频里点到为止的每一条，背后都有一段真实的工程史。

**威廉·卡汉（William Kahan）与 IEEE 754 的诞生。** 这位加州大学伯克利分校的数学家是浮点标准的主要设计者，被业内称为“浮点之父”，并因此在 1989 年获图灵奖（据 ACM 官方记载）。在标准诞生前，每家计算机制造商都有自己的浮点格式——IBM 大型机甚至用十六进制规格化浮点，不同机器算同一个题能给出不同答案，数值分析学者要做大量“机器相关”的分析工作。卡汉在 1970 年代末先以顾问身份把这套设计思想注入英特尔 8087 数学协处理器，随后主持起草标准；IEEE 754 于 1985 年正式发布，此后 2008 年修订版收入了十进制浮点格式，2019 年完成最新一次修订（据 IEEE 官方标准文档序列）。今天全世界每一颗 CPU 的浮点单元，跑的都是这套规则。

**误差分析的理论根基。** 数值计算领域有一篇被引用最多的综述：大卫·戈德堡 1991 年的《What Every Computer Scientist Should Know About Floating-Point Arithmetic》（每个计算机科学家都应该知道的浮点运算知识）。它系统证明了浮点误差不是玄学，而是可以用“向前/向后误差分析”严格框定的量。视频讲的是现象，这篇论文讲的是现象背后的数学保证。

**爱国者导弹的时钟漂移（1991）。** 海湾战争中，驻扎在沙特达兰的美军爱国者防空系统连续开机约 100 小时后未能拦截一枚飞毛腿导弹，弹体命中营房，28 名美军士兵死亡（据美国审计总署事后调查报告，编号 IMTEC-92-26）。事故根源：系统内部用 24 位定点数近似 0.1 秒的时钟节拍，累积漂移约 0.34 秒，据此推算的目标空中位置偏差数百米，雷达“搜索窗口”扑空。注意——这是**定点数**的表示误差，与 IEEE 浮点是近亲而非同一种病，但病根一模一样：有限位数存不下某些数。

**阿丽亚娜 5 号火箭首飞爆炸（1996）。** 欧洲航天局的阿丽亚娜 5 号首飞约 37 秒后偏离轨道解体，自毁系统引爆，四颗卫星报废，损失以亿美元计（据事故调查委员会“狮子报告”）。直接原因：火箭复用了阿丽亚娜 4 号的一段对准程序，其中把一个 64 位浮点数（水平速度）转换成 16 位整数时——5 号飞得比 4 号快得多——数值超出 16 位整数上限，触发异常，两套惯性导航系统接连停机。这是**类型转换溢出**，不是精度丢失，又是另一种“数字格式”病。

**英特尔奔腾 FDIV 查表错误（1994）。** 数学家 Thomas Nicely 发现奔腾处理器算除法偶发出错，追查到浮点除法电路的查找表有五项被错误留空；风波之后英特尔宣布召回，为此计提约 4.75 亿美元（据当时财报与媒体报道）。这一课说明：即使有了 IEEE 754，**硬件实现**仍可能背离标准——契约签了，还得有人验收。

**Excel 的 850×77.1=100000（2007）。** 微软表格软件曾被用户发现这个著名的乘法错误：乘积本应是 65535，浮点运算把它算成 65534.999…，而 Excel 在把这个数转回十进制显示时，恰好在这个特殊数值区间走了岔路，屏幕上打出 100000（据微软当时的官方说明与修复公告）。这是**显示层的 bug**，底层数值其实没错——又一个被算在“浮点”头上的冤案。

**AI 时代的新剧情：精度成了可交易的资源。** 到今天，剧情又翻了一面：AI 芯片在主动追求**更粗**的数字。深度学习训练早已把 16 位浮点（FP16/BF16）当标配，英伟达 H100 之后的主流加速卡把 8 位浮点（FP8）做成硬件原生动员格式，DeepSeek 等团队也公开报告过用 FP8 完成大模型训练（据各厂商技术报告）；推理端甚至开始探索 4 位格式。逻辑很直白：神经网络对“每个数多两位尾数”不敏感，而对算力和带宽极度敏感——精度变成了可以按需出售的预算。**当年卡汉拼了命要“锁死”的确定性，如今成了工程师主动议价的筹码**，这是视频拍摄时还没上演的续集。

## 批判性审查：哪些是真病，哪些是背锅

把视频的主张和外部证据对照，可以划出一条清楚的分界线。

**证据充分的部分**：二进制表示不了 0.1、双精度约 16 位十进制、数轴网格随数值变大而变疏、JS 的 2^53 整数悬崖——这些都可以在任何一台机器上当场复现，属于计算机科学的基础事实，没有任何争议。

**容易被夸大的部分**是“计算机不会算数”这个流传甚广的印象。事实恰恰相反：IEEE 754 的每一次加减乘除都是**正确舍入**的，误差有严格上界，跨硬件结果一致。浮点数的“不可靠”，绝大部分不是标准失职，而是使用者把它当实数用的期待落空——就像尺子本来就刻到毫米，你非要说它量不准头发。戈德堡论文的核心论点正是：浮点误差可以被分析和控制，工程上完全“可靠”。

**流传中最常见的张冠李戴**也值得点名：爱国者事故是定点数节拍误差，阿丽亚娜是整数转换溢出，Excel 是显示层缺陷——三个著名“翻车现场”严格说都不是 IEEE 浮点精度本身的错，但在传播中统统被归进“浮点不靠谱”的筐里。理解它们的真实病理，比记住“计算机数学很玄”有用得多。

**视频没讲的补充**：标准本身在 2008 年就收入了十进制浮点（decimal128 等格式），专为金融场景设计；C# 的 decimal 类型底层就是十进制的。也就是说，“钱不能用浮点算”这个结论，标准委员会早就听到了，并且给了官方答案——只是多数教程忘了提。

## 认知提升与实用启示

**第一，永远不要用 `==` 比较浮点数。** 这是这份契约最直接的推论：既然每个数都是近似值，相等判断就该问“差得够不够小”，而不是“是否严格相等”。实践中用相对误差容差，例如 `|a-b| ≤ max(|a|,|b|)×ε`，或者直接用语言内置工具——Python 的 `math.isclose()` 就是为此而生。循环累加判断收敛、判断两个计算路径的结果是否一致，同理。

**第二，钱的事，别交给二进制。** 金额要么用最小单位整数（以“分”记账），要么用十进制类型（Python 的 `decimal`、Java 的 `BigDecimal`、C# 的 `decimal`）。商业系统里 `0.1 + 0.2 ≠ 0.3` 不是段子，是对账差一分钱查一夜的开始。同理，凡是“必须精确到某个整数刻度”的量——积分、库存、配额——都优先考虑整数或定点方案。

**第三，大量累加时提防“大数吃小数”。** 往一个已经很大的和里反复加小量，小量会掉进大数的缝隙里被吞掉。对策有三：把数按大小排序、先加小的；分块求和再合并；或者直接用 **Kahan 求和算法**——用一个补偿变量把每次被舍入丢掉的零头记下来、下次找补回来，几十行代码就能把百万次累加的误差压低几个数量级（这正是卡汉本人的手笔）。科学计算默认上双精度，不要为了省内存用单精度存中间结果——除非你清楚自己在交易什么。

```text
Kahan 求和的骨架：
sum = 0; c = 0          # c 记录每次被丢掉的零头
for x in numbers:
    y = x - c           # 先把上次的零头找补回来
    t = sum + y
    c = (t - sum) - y   # 本次又被丢掉多少，记下来
    sum = t
```

**第四，把精度当预算来花。** 这是最新的一条认知：精度不是越高越好，而是成本结构的一部分。写图形、训练模型、跑推理时，主动选择 FP16/FP8 换吞吐；写结算、导航、科学仿真时，坚决上十进制或双精度。看清每个数字格式的“刻度”和“价格”，才是这条视频最终极的读懂方式——从“为什么算不准”进化到“我该让它多准、为此付多少”。

## 知识结构图

```mermaid
flowchart TD
    A["0.1+0.2≠0.3"] --> B["十进制小数在二进制下除不尽"]
    B --> C["有限位数必须舍入"]
    C --> D["IEEE 754：符号+阶码+尾数"]
    D --> E["隐藏位白赚一位精度"]
    D --> F["数轴网格越往右越疏"]
    F --> G["2^53 整数悬崖"]
    F --> H["大数吃小数"]
    C --> I["历史翻车：爱国者/阿丽亚娜/Excel"]
    D --> J["正确舍入：误差有上界"]
    J --> K["工程结论：误差可预算"]
    E --> K
    G --> K
    H --> L["对策：容差比较/十进制/Kahan求和"]
    K --> L
    K --> M["AI时代：精度当预算花"]
```

## 延伸阅读

1. **0.30000000000000004.com** — 一页讲清浮点误差的迷你网站，适合当快捷参考丢给还在困惑的同事。
2. **Python 官方教程《Floating Point Arithmetic: Issues and Limits》**（docs.python.org/3/tutorial/floatingpoint.html）— 官方用一整章诚实交代“为什么你的小数算不准”，是目前可读性最好的权威短文之一。

## 参考来源

1. [Computerphile: Floating Point Numbers（YouTube 频道节目页）](https://www.youtube.com/results?search_query=Computerphile+floating+point+numbers) — 本文解读的视频本体，频道页面可查看实时播放数据
2. [IEEE 754 - Wikipedia](https://en.wikipedia.org/wiki/IEEE_754) — 浮点标准的格式定义、1985/2008/2019 各版本沿革
3. [William Kahan - Wikipedia](https://en.wikipedia.org/wiki/William_Kahan) — 标准主设计者生平、1989 年图灵奖与 8087 前史
4. [MIM-104 Patriot - Wikipedia](https://en.wikipedia.org/wiki/MIM-104_Patriot) — 达兰拦截失败与定点数时钟漂移（GAO 报告 IMTEC-92-26 摘要）
5. [What Every Computer Scientist Should Know About Floating-Point Arithmetic（Goldberg, 1991）](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) — 浮点误差分析的奠基性综述，Oracle 托管全文

---
> 本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵**爱卫生**（微信ID: gprshome201101）维护，聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。
> 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
