如果上帝给我32个位 - 深度理解双精度浮点数

上帝给了我32个位,让我重新设计数字的存储方式。为了大家学起来不头疼,我就这么设计:

第一位留给正负号,剩下的,16位用于表示整数部分,15位用于表示小数部分,设计完毕。

固定小数点

突然有几个人找到我,告诉我他们遇到了问题。

彩票中心:整数位只有16位,最大能表示65535, 一百万不到!我们这里一秒钟都几个亿的资金流,怎么处理?

芯片制造商:我们这的工艺都是纳米级的(0.000001毫米),你的小数位只有15位,精度就到0.00003(1 / 2^15),差太多了!

物理学家:光速(约300000000)和引力常量(约0.0000000000667)我们都需要,你自己看着办吧。

上帝只给了32个位,多要点不合适,那么只能重新设计了。既然固定小数点的设计不能满足大多数的需求,我们就让它浮动起来,遇到大数的时候多给整数留位置,遇到小数的时候多给小数留位置。

怎么实现浮动呢?

对于十进制数 567.0

  • 小数点向右浮动1位可以看成 567.0 * 10^1 = 5670.0
  • 小数点向左浮动1位可以看成 567.0 * 10^-1 = 56.7

我们发现利用10^n 次方可以控制小数点的浮动,其中指数n决定了小数点的浮动方向和移动次数。

这个性质对于二进制同样适用。

可以用2^n 次方来控制小数点的浮动,其中指数n决定了小数点的浮动方向和移动次数。那么我们就从32位中留下一些位置,用来保存指数n的信息,剩下的用来保存数的信息。

于是这样一个设计轮廓出现了:

符号位 - 1 位
指数位 - 8 位
尾数位 - 23 位

无固定小数点

轮廓已经出现了,现在我们来完善一些细节。

A. 任何一个十进制数都可以转换成对应的二进制
10.0 --> 1010.0
4.0 --> 100.0
1.25 --> 1.01
0.125 --> 0.001

十进制小数转二进制

B. 除了0,任何一个二进制数都可以看成如下形式
1010.0 - 将1.01的小数点向右浮动3位
100.0 - 将1.0的小数点右浮动2位
1.01 - 将1.01的小数点右浮动0位
0.001 - 将1.0的小数点左浮动3位

C. 从A和B我们总结出:对于一个十进制非零数n,都可以由对应的二进制数1.xxxxxxx... 浮动小数点得到。

于是我们可以把小数点的浮动信息保存在指数位,而1.xxxxxxx... 则保存到尾数位。

指数位

怎么用8个位来保存浮动信息呢?
8个位能表示255个数

  • 我们用一半的数来代表右移动一位,两位,三位…
  • 用一个数来代表右移零位
  • 用剩下的数来代表左移一位,两位,三位..

于是这样规定

...
10000010 (130)- 代表向右浮动3位
10000001 (129) - 代表向右浮动2位
10000000(128) - 代表向右浮动1位

01111111(127) - 代表向右浮动0位

0111110(126) - 代表向左浮动1位
01111101 (125)- 代表向左浮动2位
01111100 (124)- 代表向左浮动3位

尾数位

由C得出,除了0,尾数位保存的二进制数都是形如1.xxxxxxx... 的二进制数字,于是我们将起始的1 省略,只将xxxxxxxxxxxx放入尾数位中,读取时再补上起始位的1。即1.01只将01保存到尾数位中。这样无形中将23位变成了24位,增大了一倍的存储范围。

对于0,还有其他一些特殊情况,我们稍后处理。

主要规则都设计完毕了,我们来看看十进制的10和0.125在内存中怎么表示。

10

  1. 将10 转换成二进制 1010.0
  2. 看成 1.01 向右浮动3位


    十进制10在内存中的表示

a. 指数位为10000010,代表右浮动3位
b. 尾数为位01,补上起始1为1.01
可得二进制1010.0, 转十进制得10

0.125

  1. 0.125 转换成二进制 0.001
  2. 看成1.0 向左浮动3位
十进制0.125在内存中的表示

a. 指数位为01111100,代表左浮动3位
b. 尾数为位0,补上起始1为1.0
可得二进制0.001, 转十进制得0.125

更多示例

特殊情况
  1. 指数位全为1,此时如果:
  • 尾数位全为0
    表示无穷大,由符号位决定正负无穷大。
  • 尾数位不全为0
    表示NaN
  1. 指数位全为0。此情况下尾数位读取时不再补1,而成为形如0.000000的数。此时如果:
  • 尾数位全为0
    表示0
  • 尾数位不全为0
    表示接近0的很小的数字

这种存储方式就是单精度浮点数的存储方式,能表示的最大安全整数是2^24 - 1 = 16777215,
小数精度1 / 2^24 约等于 0.0000005。似乎只勉强满足了芯片制造的需求。

但是,

当我们把指数位从8位扩大到11位,尾数位从23位扩大到52位,加上符号位全长64位。这就是双精度浮点数的存储方式了
最大安全数为 2^53 -1 = 9007199254740991
小数精度1 / 2 ^ 53 约等于 0.0000000000000001

参考文献
THE FLOATING-POINT GUIDE
浮点数的二进制表示

其他
designare-table: 企业级react table组件, 兼容IE11
迅速搭建React源码测试环境
React Proptypes 转 React Typescript 工具

你可能感兴趣的:(如果上帝给我32个位 - 深度理解双精度浮点数)