加深理解代替单纯记忆
本文转述了《深入理解计算机系统》一书中Float Pointing章节的内容,以期对浮点数的内部结构有深入的理解
Floating Point
IEEE规定了浮点数的二进制的存储表达规范,可以用于表示形如V = (-1) ^ s * M * 2 ^ E的浮点数
其中
- s(sign)表示正负,0为正,1位负
- M表示有效位数(significand),或者成为小数部分(fraction)
- M可以表示
1.xxx和0.xxx样式的值
- M可以表示
- E表示指数部分
对应单浮点数(float)和双浮点数(double),其在内存中的存储样子是这样
注意第三行表示的是双浮点数0-32位部分
- 两类浮点数符号位都只占1位
- 单浮点数,8位用于表示指数部分,23位用于表示小数部分
- 双浮点数,11位用于表示指数,52位用于表示小数
二进制数转换到最终小数
最终的小数值,并不是将上面内存中二进制的数转为十进制,再套上前面
V = xxx公式的结果。中间可能需要做一些转换,还有一些特殊情况要考虑
根据exponent部分不同的取值,浮点数可以分为三种case
Normalized Values、Denormalized Values、Special Values
最终的小数值,是根据这三种不同情况,得出最终的s、M、E,再套用公式计算后的结果
Normalized Values
要求exponent部分既不全都是0,也不全都是1。该类型的值最普遍
- 该情况下
E = e - Bias- e是exponent区域二进制数的无符号十进制值
- Bias是一个偏移量值,单浮点数时是127,双浮点时是1023(Bias的值是
2^k - 1计算得来,k表示e部分所占的位数) - 单浮点时E的取值是
-126-127,双浮点数是-1022-1023
- M的值可以表示为
M = 1 + f- f就是fraction部分表示的二进制小数
- 之所以要加1,其实是为了实现让1 <= M < 2,同时又不想占用fraction部分的位置,取巧的做法
- 上面取巧的做法叫做
implied leading 1
Denormalized Values
要求exponent部分全是0
- E部分值
E = 1 - Bias - M的值和f部分的二进制值含义一致
- Denormalized Values有两个作用
- 用于表示0,因为
Normalized Values下,M的值始终是1.xxx,无法表示0. - 表示0时--sign部分决定正负,其余部分全是0。IEEE规定+0和-0在有些情况下含义不同
- 另一个作用是更适合表示很接近0的值
- 用于表示0,因为
Special Values
exponent区域都是1时,表示一些特殊值(Special Value)
- fraction部分全是0时,则表示无穷大(
Infinity)- sign部分是0时,表示正无穷大
- sign是1时,表示负无穷大
- 通常,无穷大表示越界(overflow),比如两个大数相乘、0作为除数
- fraction不全是0时,表示
NAN,即not a number- 该值用于表示,通过正常的计算规则无法得出正常结果的情况。比如对-1开根号,正负无穷加正无穷
Rounding
由于IEEE对二进制浮点数表示的规范限制,我们知道计算机实际上能表示的浮点数是可以枚举的,当我们所需的精确的浮点数无法在计算机中表示时,或者我们要对浮点数部分有效位时,此时就需要rounding--截断
下面先通过十进制的数字(以十进制金额距离)来解释一下四种rounding模式,后面会再对二进制的浮点数进行说明
| Mode | $1.40 | $1.60 | $1.50 | $2.50 | $-1.50 |
|---|---|---|---|---|---|
| Round to even | 1 | 2 | 2 | 2 | -2 |
| Round toward zero | 1 | 1 | 1 | 2 | -1 |
| Round down | 1 | 1 | 1 | 2 | -2 |
| Round up | 2 | 2 | 2 | 3 | -1 |
- Round to even
- 向最接近的值截断
- 若当前值正好处中前后两值中间位置,则向偶数截断
- Round toward zero 向靠近0的方向截断
- Round down 向值小的方向截断
- Round up 向值大的方向截断
理解了几个模式后我们再看一下二进制下浮点数的rounding行为
默认的rounding模式是 Round to even
在浮点数中,使用Round to even时,当值正好处在前后值的中间位置时,截断时认为0是even,1是odd,具体看下面例子
假设使用Round to even模式,对下面的值保留小数点后两位,截断结果如表所示
| 值 | Round to even |
|---|---|
| 10.00011 | 10.00 |
| 10.00110 | 10.01 |
| 10.11100 | 11.00 |
| 10.10100 | 10.10 |
分析过程
既然保留小数点后两位,我们只需要看从第三位开始的值即可
- 10.00011,10.00的下一个值是10.01,从10.00到10.01,中间值是10.001,那么10.00011比10.001小,所以结果应该是10.00
- 10.00110可根据上面一条同理分析
- 10.1110,10.11的下一个值是11.00,从10.11到11.00,中间值是10.111,那正好处在了中间值位置,我们选择偶数,也就是最终结果中最后一位是0的,即11.00
- 10.10100,10.10到下一个值10.11,中间值是10.101,选择偶数,即为10.10