ChenPt / dailyNote

dailyNode for myself
https://github.com/ChenPt/dailyNote/issues
0 stars 0 forks source link

4/16/JavaScript浮点数精度问题。 #10

Open ChenPt opened 6 years ago

ChenPt commented 6 years ago

关于浮点数

浮点数分为单精度和双精度。在C语言中,单精度对应的是float类型,双精度对应的是dobule类型,两者的区别就是单精度浮点数在计算机中的存储字节为4个字节也就是32位,双精度浮点数在计算机中的存储字节为8个字节,也就是64位。 根据IEEE规范,两者都是由三部分组成,且格式如下 符号位,指数位,尾数位(小数,一般讨论的精度就是指的这个) 取值范围取决为指数位,计算精度取决为小数位

符号位(1 bits) 指数位(8 bits) 尾数位(23 bits)

对于单精度浮点数,符号位所占二进制位为1,指数为8位,尾数(小数)为23位。

符号位(1 bits) 指数位(11 bits) 尾数位(52 bits)

对于双精度浮点数,符号位所占二进制位为1,指数为11位,尾数(小数)为52位。

偏差值

对于指数位,因为需要考虑负指数和正指数,所以引入了一个偏差值,实际的指数值按要求需要加上一个偏差(Bias)值作为保存在指数域中的值。 偏差值的计算公式为 为什么是这个数呢?

偏差值的计算

首先,如果我们只有4个二进制位来存储数字,我们最多可以存储多少? 首先一个二进制位有两种取值,0或1,那么我们其实就可以存储2^4 = 16个不同的数字。 对于非负整数,其范围就是[0, 15] 对应二进制为[0000, 1111] 而如果考虑到负整数,对于十进制来说其范围可能是[-1, 14][-7, 8][-8, 7]等等,而对于二进制,其范围还是[0000, 1111]

对于[-7, 8][0000, 1111]-7 这里用 0000(2)来表示,那么0011(2)表示多少呢? 因为0011实际上表示的是3, 而前面0000表示的是-7,那么我们可以推出0011其实表示的是3-7 = -4。 这里的7,就是我们所说的偏差值了。同理,对于[-8, 7],其偏差值为8。 这个范围区间怎么确定呢?其实是没有一个标准来确定的,通常是使得非负整数的个数与负整数的个数一样,也就是[-8,7]这种格式, 负整数范围为[-8, -1], 非负整数取值范围为[0, 7],两者都是8个。 而对于IEEE-754标准,它采用的是非负整数比负整数多出两个数字的区间范围分配方式,也就是[-7,8] 这种形式的。 对于非负整数和负整数个数相同的区间范围来说,计算偏差值就直接取数字个数的一半就好了,也就是 $2^(n-1)$

对于单精度浮点数来说,偏差值为127,所以指数取值范围为[-127, 128]。 如果再去掉全0以及全1的情况,那么就指数范围就只剩下[-126, 127]之间。同理,双精度浮点数偏差值为1023,指数取值范围为[-1023,1024]。所以实际的指数值,为指数域中的值减去偏差值

IEEE标准要求小数点左侧必须为1,所以可以省略小数点前这个1,腾出一个二进制位来保存更多的尾数,所以对于单精度浮点数计算精度可以达到24个二进制位,而对于双精度浮点数计算精度可以达到53个二进制位。

JavaScrtipt的所有数字都保存为64位浮点数。