浮点数的奥秘
经过烧脑的课程,琢磨了好久,终于明白了浮点数是怎么存储的。
一、先认识一下浮点数
1.1 存储格式
以单精度浮点数float为例,存储规则为。
1.2 实例查看
我们看一下16.9这个数字存储是什么样的二进制,可以使用以下代码:
Float f1 = 19.9f;
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f1)));
得到结果:
1000001100111110011001100110011
看到只有31位,其实第一位的符号位为0,没有打印出来,其实是
01000001100111110011001100110011
根据存储格式我们得知指数位为100000110,而有效数字为0111110011001100110011。 这几个数字怎么得来的,我们详细分析一下。
二、浮点数存储规则
这一节首先给出浮点数存储的实际规则,并从正反两个方面解析。
2.1 浮点数规则
有效数字
跟根据1.1中的存储格式,23位有效数字位存储的是小数部分,整数部分肯定是个1。这样相当于根据约定,可以多一位的精度。
指数位
指数位采用移码处理后存储无符号位的整数,我们列个表来说明。 约束八个0和八个1不能用。
| 动作 | 最小值 | 最大值 |
|---|---|---|
| 原始补码表示 | -128 | 127 |
| 移128 | 0 | 255 |
| 移128去掉八0八最终表达范围 | -127 | 126 |
| 移127 | -1 | 254 |
| 移127去掉八0八最终表达范围 | -126 | 127 |
移动127后,八个1是 原来的(-128)10000000 + 01111111 = 11111111; 0 是 原来的(-127)10000001 + 01111111 = 00000000; 这样相当于去掉了两个最低位,可以保证数据表达的上限。
规则分析完了,真是感叹设计者利用规则来榨取32bit存储数据的极限能力。
2.2 反向分析
我们反向分析,看从二进制怎么推导出10进制数。
浮点数19.9f表示为:01000001100111110011001100110011
第一位符号位为0, 正数。
指数位是:10000011(131), 反向移码相当于131-127=4,也就是指数位是4
有效数字,加上省略存储的1,那就是1.00111110011001100110011。
有效数字往左移动指数位数4,就是10011.1110011001100110011,可以看到整数位10011,就是19, 小数位0.5+0.25+0.125 (0.0625)(0.03125) + 0.015625 = 0.890625 后面再续上,基本接近于0.9了。
2.3 正向分析
正向分析,我们看怎么从10进制数推导出二进制数。
19.9f, 首先整数位置变换为10011;小数位采用乘2取整法。
| 数据 | 乘2结果 | 取整 |
|---|---|---|
| 0.9 | 1.8 | 1 |
| 0.8 | 1.6 | 1 |
| 0.6 | 1.2 | 1 |
| 0.2 | 0.4 | 0 |
| 0.4 | 0.8 | 0 |
| 0.8 | 1.6 | 1 |
| 0.6 | 1.2 | 1 |
| 0.2 | 0.4 | 0 |
| 0.4 | 0.8 | 0 |
那么就得出整个二进制数字位是10011.111001100。
首位变成1,需要移位4,那么指数位就是4,移码加上127就是131;那么指数位就是
10000011
数字变成1.0011111001100,省略首位存储,那么有效数字就是
0011111001100
三、0的表示
这个是我的假设:
表示0时,符号位是八个0,此时不再计算有效数字省略的1.
学习浮点数的感悟
-
bit使用设计上榨取存储能力,
-
巧妙的设计,提高计算机计算的效率。