二进制小数

二进制小数可以理解成在实数范围内的二分查找逼近的过程。

binary frac

IEEE 浮点数

  符号 阶码 小数 阶码偏置 非规格阶码
Float (32) 1 8 23 127 -126
Double (64) 1 11 52 1023 -1022

规格化

阶码区域不全为零或全为一的情况下,是最常见到的。

阶码被解释为有符号数,通过减去一个偏置值 \(Bias_{32}=2^7-1=127\) 或 \(Bias_{64}=2^{10}-1 = 1023\) 的方法进行,也就是说是从负数开始的,而不是像整数的有符号表示方法:先从正数开始,一半之后再解释为负数。

阶码用于表达科学技术法中二的次数,也就是说 \(2^E\)。

在规格化的情况下,我们总能调节阶码使得小数部分以 \(1.???\) 开头,所以开头的1就不需要记录了,只需要小数部分。从来都是 1+小数部分所表达的位。

其他

非规格化的值,阶码全为 0,小数位不包含隐含的 1,所以可以用来表达 \((-1,1)\) 之间的数字。同时因为符号位的缘故,浮点数中有正负零。在这里,所乘上的是 \(2^{-126}\) 或者 \(2^{-1022}\) 。

NaN 和 \(\infty\) 是两种特殊值,在阶码全为 1 的时候出现。

如果小数部分全为 0 就是 \(\infty\),如果不全为零就是 NaN,前者可以表示溢出,后者可以表示非法操作。

但是我不喜欢 NaN 的设计因为它破坏了浮点数的全序特性,使得很多算法处理 NaN 会出现问题,特别是排序算法。感觉用中断就可以了。

从非规格化到规格化

如果观察非规格化的阶码会发现,表示范围并不是 \(2^{-127}\) 或者 \(2^{-1023}\) ,这样的设计是因为,非规格化值没有隐含的 1,而规格化值有,借助隐含的 1 能让非规格化值转换到规格化值的时候只需要通常的进位规则就可以了。

舍入

除了向上、向下、向零舍入,还有一种,也是浮点数默认的舍入方式,是向偶数舍入。

如果是偏向某个值的数,舍入的时候会舍入为靠近它的值,比如说 1.4 会舍入成 1,而 1.6 会舍入成 2。

但是对于中间址,向偶数舍入会将中间值舍入最近的偶数,1.5 会舍入成 2,但 2.5 也会舍入成 2。

我们学校教的时候习惯四舍五入,这样的缺点是舍入的误差会使得结果趋向于更大,而五舍六入会让结果变得更小,向偶数舍入会从概率上尽量减少舍入导致的统计偏差。

当然向奇数舍入也行,但是毕竟偶数更讨人喜欢不是吗?

计算

浮点数的计算保证单调性,封闭性,和可交换性,但不保证结合性和分配性。这也是浮点数 bug 的源泉之一。