⚙️ 第三章 计算机在计算小数时会出错的原因
📄 一句话概括

两个省心的设计:规格化让尾数开头的 1 不必存(白赚一位精度);移码让指数不必管正负(能直接比大小)。

📝 本节和前面几节的关系

3.4 认了三个字段,这一节说清指数位和尾数位各自用什么规矩来填。

一、规格化:省掉开头的那个 1

任何二进制数规格化之后,尾数一定是 1.xxxx 的样子(第一个有效位永远是 1)。 既然它永远在,那就干脆不存它——省下一位。 例:11.25 = 1011.01(二)= 1.01101 × 2³,尾数部分只存 01101。 这就是为什么 23 位尾数实际能表示 24 位有效数字——那一位是白赚的。

二、代价:0 表示不了

规格化要求尾数开头是 1,那【0】怎么办? 所以标准里给 0 单独留了位置(指数和尾数全为 0 就表示 0)。 顺带还腾出了几个特殊组合:正负无穷、以及【不是数】(NaN)。 所以浮点数的 0 有两个(+0 和 -0),但它俩比较起来是相等的。

三、移码:让指数能直接比大小

指数有正有负。可如果按补码存,比较两个指数大小就得多做判断。 移码的做法很简单:存【真实指数 + 偏移量】。 单精度偏移量是 127,双精度是 1023。 例:真实指数 3,单精度存的就是 3 + 127 = 130(二进制 1000 0010)。

四、换了移码之后好在哪

存进去的都是非负数了——可以直接按无符号整数比大小,硬件省一堆判断。 代价是读的时候要记得减回去:看到 130,要反应过来它其实是 3。 所以浮点数的位模式里,每个数字都不是肉眼看到的那个值——这正需要下一节的工具去拆。

五、动手 Demo:规格化与移码——两个省心的小设计

规格化:把尾数统一写成 1.xxxx 的样子,开头那个 1 干脆不存。 移码:指数存的是【真实指数+偏移量】,这样正的负的都能比大小。

v = 11.25
print("二进制    ", format(int(v), "b") + ".01")
print("规格化    1.01101 x 2^3")
print("尾数      01101 -> 补到 23 位")
print("指数      3 + 127 =", 3 + 127)
print("拼起来    0 10000010 01101000000000000000000")

实跑输出:

--------------------------------------------------------------------
规格化与移码——两个省心的小设计
--------------------------------------------------------------------

**规格化**:把尾数统一写成 1.xxxx 的样子,开头那个 1 干脆不存。
**移码**:指数存的是【真实指数+偏移量】,这样正的负的都能比大小。

  拿 11.25 走一遍单精度的流程:

  步骤                               结果
  ----------------------------------------------------------------------
  先写成二进制                       1011.01
  规格化:小数点移到第一个 1 后      1.01101 × 2^3
  尾数:省掉开头的 1,补到 23 位     01101000000000000000000
  指数:真实值 3 加偏移量 127        130 = 1000 0010
  符号:正数记 0                     0
  拼起来                             0 10000010 01101000000000000000000

  两个设计省下了什么:
    规格化   白赚一位精度——开头的 1 不用存,却始终在
    移码     指数不必管正负号,**直接按无符号比大小就行**

  **省下的每一点都要还:** 规格化要求尾数不能是 0(所以有了两个特殊的零),
  移码的偏移量(单精度 127、双精度 1023)也成了必须记住的常数。

规格化白赚一位精度(23 位尾数能表示 24 位有效数字),移码让指数能直接比大小。 代价是 0 得单独约定,读指数时必须记得减去 127/1023。

30 秒实验

  1. 把 6.5 走一遍单精度流程:先写成二进制,再规格化,再算指数。
  2. 看到指数位 01111100,减掉 127,真实指数是多少?

常见误区

  1. 以为尾数存的是完整有效数字。开头那位 1 是隐含的,不占位。
  2. 直接拿指数位的值当真实指数用。必须减去偏移量(单精度 127)。
自测三问(点开对答案) 1. 规格化省下了什么? —— 尾数开头永远是 1,可以不存,于是 23 位尾数能表示 24 位有效数字。 2. 移码是什么意思? —— 指数存的是真实指数加一个固定偏移量(单精度 127、双精度 1023)。 3. 为什么要用移码存指数? —— 存进去都是非负数,硬件可以直接按无符号整数比大小,省掉正负判断。

相关笔记