<!-- title: 浮点神课深读：小数装不进二进制 -->
<!-- domain: programming -->

# 浮点神课深读：小数装不进二进制

## 概述

程序员入职第一周就会遇到那个著名的灵异事件：`0.1 + 0.2` 不等于 `0.3`，而是一个长达十七位的 `0.30000000000000004`。绝大多数人的反应是背下结论——“浮点有精度问题”，然后一辈子没弄清精度问题到底出在哪一位上。Computerphile 频道的经典视频《Floating Point Numbers》就是补这一课的：主讲人 Mike Pound 拿着纸笔，把“电脑如何存小数”这件事从二进制的第一性原理推了一遍。这条视频上线十年来长期是该频道播放量最高的一批视频之一，“0.1 加 0.2”更是程序员社区永远复读的梗，甚至催生了专门的网站 0.30000000000000004.com。本文按视频的叙事顺序拆解其论证，补上视频没讲到的理论出处与真实工程事故，并给出可以直接落地的避坑建议——看完这篇，你对浮点数的理解会比看完视频还多一层。

## 一、视频基本信息

| 项目 | 内容 |
|------|------|
| 视频标题 | Floating Point Numbers（浮点数） |
| 频道 | Computerphile |
| 主讲人 | Dr. Mike Pound（诺丁汉大学计算机科学系） |
| 上线时间 | 2014 年前后上线，长期常青 |
| 热度 | 数百万播放量级；“0.1+0.2”话题在 Reddit、Twitter/X 等社区常年被反复引用 |
| 内容形式 | 纸笔手推式白板讲解，约 20 分钟 |

> 注：本篇制作时未能实时抓取频道页面，上述时间与热度为量级描述，精确数字请以频道页面为准。

## 二、核心主张拆解：视频到底讲了什么

Mike Pound 的讲法非常克制：不用 PPT，不堆术语，就是从“二进制怎么表示数”开始，一步一步推到“为什么 0.1 会出事”。整条视频的论证链条可以拆成五步。

### 要点一：整数在二进制里是精确的，分数不是

视频的起点是让听众安心：整数部分没有问题。8 个比特可以无损地表示 0 到 255，因为整数的二进制表示是有限的。真正的问题从“小数点”出现的那一刻开始。二进制里小数点后的第一位是 1/2，第二位是 1/4，第三位是 1/8——每一个位置只能是十进制分数 1/2 的幂次。这意味着：**凡是能写成 2 的幂次之和的小数，二进制存得干干净净（比如 0.5、0.25、0.375）；凡是不能的，比如十进制的 0.1，二进制里就是无限循环小数**。0.1 换算成二进制是 0.000110011001100…，那个 0011 永远循环下去。而计算机的存储位数是有限的，无限循环必须在哪里停下来——这就是一切事故的种子。

### 要点二：定点数的死胡同，逼出了“浮动”的小数点

视频顺着推：既然只能存有限位，最朴素的方案是“定点数”——固定小数点后保留 N 位，多余的统统截掉。但定点数有个致命缺陷：它把表示范围锁死了。你想表示原子直径那么小的数，也想表示国家预算那么大的数，定点数就得保留同样多的位数，极其浪费。Mike Pound 用了一个非常直观的类比：**科学家写大数从来不写一长串零，而是写科学记数法**——6.022 × 10²³。真正携带信息的是“6.022”这几位有效数字和“23”这个指数。把这套思路搬进二进制，让小数点可以“浮动”，就是 floating point 这个名字的由来：小数点的位置不再固定，而是由一个专门的“指数”字段来指挥。

### 要点三：IEEE 754 的版图——符号、指数、尾数

视频的核心段落是在纸上画出浮点数的内存布局。以 32 位单精度（float）为例，32 个比特被切成三段：

- **1 位符号位**：正数还是负数；
- **8 位指数**：小数点浮动到哪里（实际存储时偏移了 127，即“移码”，这样指数的正负都能用无符号数存）；
- **23 位尾数**：有效数字部分。

这里藏着视频里最精巧的一个细节：**隐含的前导 1**。规格化的二进制科学记数法里，有效数字永远以 1 开头（就像十进制科学记数法永远以非零数字开头），既然永远是 1，那就没必要存它——23 位尾数实际能表达 24 位的精度，凭空多赚一位。讲完单精度，视频顺带对比了 64 位双精度（double）：1 位符号 + 11 位指数 + 52 位尾数，十进制有效数字从约 7 位跃升到约 15~16 位。这也是 Mike Pound 给普通开发者的第一建议：没有极端性能压力，就用 double，它能帮你躲掉绝大多数精度尴尬。

### 要点四：精度的悬崖——大数吃小数

理解了布局，很多“灵异现象”瞬间可以自己推导出来，不用背。视频演示的核心机制是：**两个浮点数相加时，硬件必须先对齐指数，把小的那个数的尾数右移**。右移就会丢位。当两个数大小悬殊时，小数会被“吃得一根毛都不剩”——比如给一个很大的数反复加 0.0001，加了一万次，结果可能纹丝不动，因为 0.0001 在对齐指数时被整个舍入掉了。这直接推出两个结论：第一，浮点加法**不满足结合律**，(a+b)+c 和 a+(b+c) 可以算出不同结果，加法的顺序本身会影响答案；第二，任何超出有效数字范围的十进制小数，存进电脑那一刻就已经不是“原来的那个数”了——0.1 和 0.2 各自带着一点点偏差，相加后偏差的方向恰好让你看到 0.30000000000000004。

### 要点五：视频的落点——不是 bug，是设计

很多人看完视频会把浮点误差理解为“电脑算错了”。Mike Pound 在结尾把态度摆正了：浮点运算不是算错，而是**在有限资源下做出的、被精确定义过的近似**。IEEE 754 是一份极其严肃的标准，它对每一种运算的舍入方式都做了明确规定（默认舍入到最近偶数），误差是有界的、可分析的。真正的工程问题从来不是“有误差”，而是“不了解误差就从事业务”。视频停在认知层面，而这条认知恰好是无数真实事故的分水岭——这正是下文要补的。

## 三、知识与理论溯源：这些内容来自哪里

视频本身没有给出处，但它的每一块内容背后都有扎实的文献和血淋淋的工程史。补上这层，你才知道这门课的分量。

### 1. IEEE 754：一个人推动的标准

视频里那张内存版图，来自 1985 年首次发布的 IEEE 754 标准。它的主要推动者是加州大学伯克利分校的 **William Kahan** 教授——在此之前，每家硬件厂商的浮点行为各不相同：CDC、Cray、IBM 各有各的格式和舍入规则，同一份 Fortran 程序在不同机器上能跑出不同的结果，数值软件几乎没有可移植性可言。Kahan 主导设计了这套统一的格式与舍入语义，并因此获得 1989 年图灵奖。IEEE 754 后来历经 2008、2019 两轮修订，至今仍是所有主流 CPU、GPU、JavaScript、Python 数值类型的共同地基。

### 2. Goldberg 论文：浮点领域的“圣经”

如果只读一篇浮点数文献，就是这个：David Goldberg 1991 年发表在 ACM Computing Surveys 上的《What Every Computer Scientist Should Know About Floating-Point Arithmetic》。视频里所有现象——舍入误差、大数吃小数、加法结合律失效——论文里都有严格的形式化证明和更深的展开（比如“灾难性抵消”：两个相近的数相减，有效数字会断崖式流失）。这篇论文至今仍被 Oracle 官方文档全文收录，是数值计算方向的事实入门教材。

### 3. 爱国者导弹：0.1 秒累积出的悲剧

浮点误差最沉重的一次现身，是 1991 年 2 月 25 日的沙特阿拉伯达兰。美军“爱国者”防空系统因时钟以 0.1 秒为单位累加计时——而 0.1 在二进制里同样是无限循环小数，系统用截断方式存储——运行约 100 小时后累积出约 0.34 秒的时钟偏差。对拦截弹道导弹来说，0.34 秒意味着预测位置偏出数百米，系统未能发现并拦截来袭的飞毛腿导弹，美军第 14 军需分队营房被击中，28 名士兵死亡、近百人受伤。美国审计总署（GAO）事后发布报告（GAO/IMTEC-92-26），结论直指浮点与时钟计算的软件缺陷。这条视频讲的每一个比特，在历史上都是真金白银甚至人命换来的。

### 4. 温哥华证券交易所指数：被舍入“偷走”的一半市值

另一个经典案例更贴近商业：1982 年温哥华证券交易所推出指数时，每次更新都对成分股均值做小数截断（保留三位）。单次截断微不足道，但交易日内指数上万次更新，误差单向累积，到 1983 年 11 月重新核算时，人们发现指数比真实值低了近一半——一家公司上市三年，被“舍入方式”蒸发了超过一半的账面市值。它与爱国者导弹共同说明视频要点四的本质：**单次误差无害，误差的系统性累积才是杀手**。

### 5. 被冤枉的案例：不是所有数字事故都赖浮点

这里要替浮点数说句公道话，也是多数转述文章搞混的地方。1996 年阿丽亚娜 5 号火箭首飞爆炸，常被说成“浮点溢出”，实际原因是 64 位浮点数转 16 位整数时的**整数溢出**；1999 年火星气候探测器坠毁，则是公制与英制单位混用。数字事故的形式远比“浮点不准”多样：整数溢出、单位混乱、截断误差各有各的死法。把每类误差的机制分清楚，才算真正看懂了这条视频。

### 6. 现代回声：AI 时代主动拥抱“更不精确”

视频没来得及讲的时代注脚：今天的 AI 芯片大规模使用 16 位甚至 8 位浮点（fp16、bfloat16），用牺牲精度换取吞吐量——因为深度学习对单次运算精度的容忍度远高于对算力的需求。IEEE 754 后续修订也为此加入了对应格式。同一个标准，四十年前追求“统一而可靠”，四十年后服务于“可控的不精确”，这条演化线本身就很值得玩味。

## 四、批判性审查：哪些可信，哪些要打个问号

**证据充分的部分**：视频对二进制分数、IEEE 754 布局、舍入机制的讲解与标准和教科书完全一致，属于可以放心照单全收的硬知识；0.1 不可精确表示、double 约 16 位有效数字、大数吃小数，这些都能在任何一本数值分析教材和 Goldberg 论文中得到印证。

**被社区夸大的部分**：围绕这条视频衍生出的“电脑根本不会算数”式 meme 是典型的以讹传讹。恰恰相反，IEEE 754 是精心设计到“每一次运算的误差都有严格上界”的标准，比人手算可靠得多。真正的问题从来是使用者不了解表示边界，而不是标准不可靠。

**视频为入门做了简化的部分**：第一，视频没有展开非规格化数（subnormal）、±0、无穷与 NaN 的特殊语义——比如 NaN 不等于自身、0.1+0.2 的显示值取决于语言的最短往返表示法（Python 3 的 repr 会自动显示 0.1 而不是一长串尾巴，直到你做运算才暴露）；第二，视频没有给出误差控制手段，比如补偿求和（Kahan summation）可以在累加时把丢失的低位“捡回来”；第三，视频暗示“换 double 就好”，但对金融、计费场景，正确答案不是 double，而是下一节的 decimal——十进制类型能精确表示 0.1，因为它直接按十进制位存储，这也是为什么 Java 有 BigDecimal、Python 有 decimal 模块、数据库有 DECIMAL 字段。当然 decimal 也不是万能药：1/3 在任何进制下都是无限循环，十进制解决的是“我们以十进制书写的量”的精确性，而不是消除一切循环小数。

## 五、认知提升与实用启示

**第一条：钱的事，永远不要交给二进制浮点。** 金额字段用整数（以“分”为单位）或 decimal 类型，这是计费、支付、财务系统的铁律。二进制浮点存不了 0.1，就意味着对账时总会冒出一分钱的幽灵差异。同理，JavaScript 里处理第三方接口返回的雪花 ID 这类超过 2^53 的整数时，要当字符串处理，否则 Number 类型会在 15 位之后悄悄改写你的数字。

**第二条：浮点比较不用 ==，累加要管顺序。** 判断两个浮点数相等，用“差的绝对值小于容差”的方式；容差最好同时考虑绝对阈值和相对阈值。对海量数据累加，如果结果要跨平台、跨机器复现（比如风控、结算、科学计算），要么采用固定顺序的分块归约，要么直接上 Kahan 补偿求和——记住视频要点四：浮点加法没有结合律，顺序就是语义的一部分。

**第三条：把“误差思维”带进架构评审。** 看到任何涉及计时的系统（爱国者导弹式的累加时钟）、涉及高频更新的统计值（温哥华指数式的滚动截断），问一句：这个误差是双向抵消，还是单向累积？累积路径有没有上限和定期重校准？这类问题便宜，出事之后的代价昂贵。

## 视频论证结构图

```mermaid
flowchart TD
    A[整数能精确存储] --> B[分数需要二进制小数点]
    B --> C[0.1 在二进制里无限循环]
    C --> D[位数有限必须舍入]
    D --> E[定点数范围受限]
    E --> F[科学记数法搬进二进制]
    F --> G[IEEE 754 统一格式]
    G --> H[符号 指数 尾数三段]
    H --> I[单精度约7位有效数字]
    I --> J[大数吃小数]
    J --> K[加法顺序影响结果]
    K --> L[误差单向累积成事故]
    L --> M[对策 选对类型与算法]
```

## 延伸阅读

1. David Goldberg《What Every Computer Scientist Should Know About Floating-Point Arithmetic》——浮点领域最重要的单篇论文，视频所有现象的严格证明都在这里
2. 0.30000000000000004.com——一个只为一件事存在的网站，用二十种语言演示 0.1+0.2 的“灵异现场”

## 参考来源

1. [Computerphile 频道页](https://www.youtube.com/user/Computerphile) — 视频标题 "Floating Point Numbers"，主讲 Dr. Mike Pound，为本文解读对象
2. [IEEE 754 - Wikipedia](https://en.wikipedia.org/wiki/IEEE_754) — 浮点标准的历史、格式定义与 1985/2008/2019 修订脉络
3. [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) — David Goldberg 论文（Oracle 官方收录）
4. [MIM-104 Patriot - Wikipedia](https://en.wikipedia.org/wiki/MIM-104_Patriot) — 1991 年达兰拦截失败事件及 GAO/IMTEC-92-26 报告背景
5. [0.30000000000000004.com](https://0.30000000000000004.com/) — 多语言演示 0.1+0.2 精度现象的知名站点

---
> 本文由「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)
