前言
最近测试给我提了个BUG,我看到的第一感觉就是这必须是后端的锅
本着不冤枉任何一个好人的态度,我还是自己看了下页面展示、接口回参和逻辑处理部分,最后发现,nnd竟然是我的(JS精度)问题。
想着,应该有些小伙伴挺感兴趣的,索性就把笔记更新到这里吧~~~
正文开始
1,为什么JS会有精度问题
精度问题是因为计算机在存储和计算数字时,使用的是二进制形式
。有些十进制数字在转换成二进制时无法精确表示
,比如0.1在二进制中是无限循环小数。
因此,当在JavaScript中进行浮点数运算时,就会出现精度误差。
整数和整数相乘,通常
不会出现精度问题
1.1,十进制转二进制
乘2取整法(用于小数)
js
将0.1转为二进制的过程如下:
0.1 * 2 = 0.2,无整数部分,取0
0.2 * 2 = 0.4,无整数部分,取0
0.4 * 2 = 0.8,无整数部分,取0
0.8 * 2 = 1.6,有整数部分1,取1,剩余小数部分0.6
0.6 * 2 = 1.2,有整数部分1,取1,剩余小数部分0.2
0.2 * 2 = 0.4,无整数部分,取0
... 之后的过程会不断重复
按照这种格式表示,0.1的二进制表示为:**0.0001100110011001...** ,是一个无限循环小数
除2取余法(用于整数)
js
将100转为二进制的过程如下:
100 / 2 = 50 余0
50 / 2 = 25 余0
25 / 2 = 12 余1
12 / 2 = 6 余0
6 / 2 = 3 余0
3 / 2 = 1 余1
1 / 2 = 0 余1
然后将所有余数倒序排列,得到数1100100,这就是100的二进制表示。
1.2,二进制转十进制
js
将1100100转为十进制的过程如下:
1*2^6 = 64
1*2^5 = 32
0*2^4 = 0
0*2^3 = 0
1*2^2 = 4
0*2^1 = 0
0*2^0 = 0
64+32+0+0+4+0+0=100
1.3,为什么0.1+0.2 !== 0.3 但是 0.2 + 0.2 === 0.4
js
0.1(十进制) = 0.0001100110011001(二进制) 超出计算精度,结果保留十六位小数
0.2(十进制) = 0.0011001100110011(二进制) 超出计算精度,结果保留十六位小数
0.3(十进制) = 0.0100110011001100(二进制) 超出计算精度,结果保留十六位小数
0.1+0.2这两个数在二进制表示下都存在精度误差,它们的和不等于精确的0.3,所以0.1+0.2 !== 0.3。
0.2+0.2这两个数在二进制表示下虽然也存在精度误差,但是当它们相加时,这些误差恰好抵消
了,使得它们的和等于精确的0.4,所以0.2+0.2 === 0.4。(0.1 + 0.3 === 0.4也同理)
2,JS精度有问题的案例
3,解决方案
3.1,转成整数进行计算
先乘以100化为整数,再除
3.2,转成字符串然后截取
3.3,使用第三方库
Math.js 解决大数计算和精度问题
js
math.config({ number: 'BigNumber', precision: 64 });
console.log(math.format(math.eval('0.1 + 0.2'))); // '0.3'
console.log(math.format(math.eval('9007199254740991 + 2'))); // '9.007199254740993e+15'
number-precision 解决浮点数
js
NP.plus(0.1, 0.2); // 0.3 +
NP.minus(1.0, 0.9); // 0.1 -
NP.times(3, 0.3); // 0.9 *
NP.divide(0.9, 0.3); // 3 ÷
完结
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。