可以去YouTube(不存在的网站)上看同名视频了解
你不想知道的JavaScript的数字的一切
为什么小数相加有精度偏差。
分两块理解:
一个数字如何被64位二进制保存。
**两个二进制位数字如何计算。**精度偏差主要发生在这里。
(快两个月没写博客了一直读webpack源码,先水一篇。)
webpack的文章太多了就不写了,JavaScript的数字有是有但都不深。
JavaScript使用双精度浮点64位类型IEEE754标准,Python和Java也是这个标准。
一个数字使用一个64位二进制来存储:
- 第1位:正负值。
0正数,1负数。
- 2~11位:指数。
有10位位数,可以表示0~2047之间的数(2^0+2^1+...+2^10)。 因为指数会有负值用来表示小数,所以最终要减去1023。
10000001001=2^10+2^1+2^0=1024+8+1=1033,1033-1023=10,所以最终指数P就是10。
- 12位:隐藏位。
永远为1,但数字0时为0。
- 13~64位:有效数字。
最终数字由:正负值、指数、有效数字相乘决定。
指数决定了浮点在有效数字位的位置,指数越大整数就越大,指数越小小数就越大。
指数的默认位置在隐藏位。
Number(0.1).toString(2),可以这样看一个数字的隐藏位
整数
100
100.1
整数与小数公用52位有效位数,浮点前是整数后是小数。整数越大占用位数越多,小数的精度就越低
-
**问题1:**因为数字100.1由多个2^n相加决定,这种计算方式就决定了,部分小数会无限循环,无限接近于这个数,比如0.1。
-
**问题2:**但最大有效位只有52位,且整数部分会占用有效位数。整数越大,小数有效位数就越少,精度就越少。
这就是0.1为什么有精度丢失,而0.5等就没有精度丢失的问题。其中一个原因。
0
64位浮点全都为0时,这个数为0
Infinty
无穷,指数部分10位二进制全部为1时就是Infinty。注意这时有效位数全部为0,包括隐藏位。
这就是隐藏位唯二为0的情况。
NaN
Infinty的情况下,有效位数有任意一位为1就是NaN。其实情况有很多,但是都给你统一到NaN上了,这就是为什么NaN是Number类型。
两个二进制位数字如何计算
- 分数转为二进制小数,分母除非为2的次幂,不然必是循环小数
- 十进制分数转换为二进制小数,分母有质因子5,那必是循环小数
- 将循环小数转换为有限小数:离散误差
- 过长的尾数舍入:舍入误差
- 误差大小不会超过最后一位有效位数,单精度是23位,双精度是52位。
对阶
两个数字的阶码可能不同,需要移动至相同阶码才能计算
- 小阶对大阶,移动小阶阶码。小阶阶码精度一般高于大阶阶码,但双精度最多保存52位。移动小阶能保存更高的精度。
- 阶码P相同无需移阶。
- 双精度最多保存52位,但对移出的若干位会保留,之后舍入用。多余位单精度是23位,双精度是52位。
1.1
1.0001100110011001100110011001100110011001100110011010
P=0
你会发现最前面多了一个1,那是隐藏位。
0.9
1.1100110011001100110011001100110011001100110011001101
P=-1
好了0.9+100.1开始对阶
0.9右平移1位,多出部分用0保存
0.11100110011001100110011001100110011001100110011001101
1就是多余数
尾数运算
1.0001100110011001100110011001100110011001100110011010
0.1110011001100110011001100110011001100110011001100110
10.0000000000000000000000000000000000000000000000000000
规格化
转化为1.M的格式
10.0000000000000000000000000000000000000000000000000000
向右移一位,阶码+1
(1.0)P=1
舍入处理
就近舍入,默认
多余数大于等于1000...001(单精度23位,双精度52位),尾数+1
多余数小于等于0111...111(单精度23位,双精度52位),多余数舍去
多余数等于1000...000(单精度23位,双精度52位),尾数为1加1,0则舍去多余数
朝+Infinty
正数 多余数不全为0,尾数+1
负数 无视多余数
朝-Infinty
正数 无视多余数
负数 多余数不全为0,尾数+1
朝0
无视多余数
溢出处理
主要就是Infinty和NaN的情况
好了最后总结精度丢失的原因
- 整数与小数公用这52位有效位
- 十进制分数转化为二进制小数时,部分小数无法正确表达会无限循环,无限接近于这个小数,但有效位数只有52位。这里就导致了第一次的精度丢失。
- 计算时的对阶,会导致部分精度成为多余数导致精度进一步丢失
- 舍入时的规则,也只是尽量的保证精度