把浮点数的四个字节取出来,按 1+8+23 切三段,就能亲眼看到 0.1 的尾巴被砍了一刀。
3.5 讲完了规矩,这一节动手把规矩验证一遍——把位拆开看。
一、工具:把内存里的字节取出来
Python 里用 struct 就能把 4 个字节取出来:pack 成 4 字节,再展开成 32 个位。 然后按 1 位符号、8 位指数、23 位尾数切开。 这三步对应的正是 3.4 讲的结构,没有黑魔法,就是切字符串。
二、拿三个数验算
0.5:指数位 01111110 = 126,减 127 得 -1,即 1.0 × 2⁻¹ = 0.5 ✓ 1.0:指数 01111111 = 127,真实指数 0,即 1.0 × 2⁰ = 1.0 ✓ 10.0:指数 10000010 = 130,真实指数 3,即 1.25 × 2³ = 10 ✓ 三行都对上了,说明我们对格式的理解没错。
三、再看 0.1 那一行
0.1 的尾数是 10011001100110011001101——数一数,正好 23 位。 而真值该是 1001100110011001100110011001100…(无限循环)。 末尾被砍了一刀——这就是误差的位置看得见的样子。 它的指数真实值是 -4,所以这个数实际是 1.10011001100110011001101 × 2⁻⁴。
四、这件事有什么实际用处
第一,调试时不再发懵。 看到 9.99999999999998,你知道该往哪儿找原因。 第二,判断精度够不够。 尾数只有 23/52 位,超出的位数本来就不存在。 第三,理解为什么比较要带容差。 两个【应该相等】的浮点数,尾数可能就差最后一位。 拆一次位,比读十页文档管用——这是理解浮点最快的一条路。
五、动手 Demo:把浮点数的位拆开看
用 struct 把内存里的 4 个字节取出来,按 1+8+23 切成三段。 指数真实值 = 存下的指数值 - 127。
import struct
for v in (0.5, 1.0, 10.0, 0.1, -2.5):
bits = "".join(format(x, "08b") for x in struct.pack(">f", v))
print("%6s %s %s %s (真实指数 %d)"
% (v, bits[0], bits[1:9], bits[9:], int(bits[1:9], 2) - 127))
实跑输出:
--------------------------------------------------------------------
把浮点数的位拆开看
--------------------------------------------------------------------
用 struct 把内存里的 4 个字节取出来,按 1+8+23 切成三段。
指数真实值 = 存下的指数值 - 127。
值 符号 指数 尾数(23 位) 指数真实值
----------------------------------------------------------------------
0.5 0 01111110 00000000000000000000000 -1
1.0 0 01111111 00000000000000000000000 0
10.0 0 10000010 01000000000000000000000 3
0.1 0 01111011 10011001100110011001101 -4
-2.5 1 10000000 01000000000000000000000 1
对着前三行验算一下:
0.5 指数真实 -1 -> 1.0 × 2^-1 = 0.5
1.0 指数真实 0 -> 1.0 × 2^0 = 1.0
10.0 指数真实 3 -> 1.25 × 2^3 = 10
再看 0.1 那一行——**尾数是 10011001100110011001101,末尾被砍了一刀。**
那就是它的误差:真值该是 1001100110011…(无限循环),这里只能存 23 位。
0.5/1.0/10.0 三行都能按【1.xxx × 2ⁿ】验算回去,格式理解无误。 0.1 的尾数 10011001100110011001101 末尾被砍——误差就写在这 23 位里。
30 秒实验
- 把 3.14 的 32 位拆开,看它的尾数最后几位是什么。
- 把同一个数按单精度和双精度各拆一次,对比尾数长度。
常见误区
- 以为【位模式看不懂就等于学不会】。就是切字符串,谁都能做。
- 忘记指数要减偏移量。不减的话 126 会被当成指数 126,差着十万八千里。
自测三问(点开对答案)
1. 怎么看一个浮点数的三段? —— 把 32 位按 1(符号)+8(指数)+23(尾数)切开。 2. 指数真实值怎么算? —— 用指数位的值减去偏移量,单精度减 127、双精度减 1023。 3. 0.1 的位模式说明了什么? —— 尾数只存了 23 位,后面的循环位被砍掉,这就是它的误差来源。- ☐ 下一节(3.7 如何避免计算机计算出错)
相关笔记
- 规格化表示法与移码表示法 — 拆位时用到的两条规矩
- 如何避免计算机计算出错 — 看清了误差,接下来是怎么绕开
- 🔵 计算机基础 · 索引 — 硬件底层的入门课
- ⚙️ 程序是怎样跑起来的 · 索引