几年开发经验都会有小数计算和数字字符串转为数字的场景,这两个场景总会遇到精度丢失的问题,下面就根据这两个场景分析一下原因,并给出解决方案。
关于小数计算问题
下面是一个测试例子
var num = 0.1 + 0.2
console.log(num) // 0.30000000000000004
为什么会出现 0.1 + 0.2 = 0.30000000000000004?这个是什么原因造成的呢?怎么解决 0.1 + 0.2 ! = 0.3?下面是我总结的,如果有不对的地方还请各位留言。
原因
- 0.1和0.2这两个都是浮点数
- 在js底层浮点数相加都是采用二进制来表示
- 0.1转为二进制是一个无限小数,所以在内存中取出来进行计算就造成了精度丢失
- 由此就可以知道0.1+0.2!=0.3
但是这样还有一个问题,看下面示例
var num = 0.1
console.log(num) // 0.1
如果按照上面说的那这个都是浮点数转为二进制那肯定是无限小数,这console的结果怎么是0.1呢?
这个是我看了一些文档摘录
- 0.1它先转为了二进制
- 这个时候把二进制转为十进制
- 把十进制转为字符串
- 主要是在转换的时候取了近视值
关于浮点数计算可以看看这个文档,希望这个文档对大家有所帮助
既然知道了原因,下面是我处理的方法
废话不多说,这个是我用的最简单的方法
var num = (0.1 * 10 + 0.2 * 10) / 10
console.log(num) // 0.3
我之所以采用这个方案的原因是浮点数计算采用二进制在进制转换的时候精度丢失,那我们反其道而行之,我们可以使用整数相加,先把所有的浮点数转为整数,最后在对整数除10,这样就可以解决了。
关于数字字符串转为number
下面有一个例子
var str = '12345678987654321'
console.log(Number(str) + 1) // 12345678987654320
var str = '123456789876543212'
console.log(Number(str) + 1) // 123456789876543220
var str = '1234567898765432123'
console.log(Number(str) + 1) // 1234567898765432000
我们都知道字符串数字可以进行算术运算,但是为什么这个值不对呢?其实之所以有这个原因是因为number转数字有最大范围和最小范围,它的范围是±1.7976931348623157e+308,如果超过这个范围number转换的时候会造成精度丢失,其实大概应该是±9007199254740992,也就是如果字符串的长度超过16位有可能就超过精度了。
js内置方法BigInt进行算术运算
var str = '12345678987654321'
console.log(BigInt(str) + BigInt(1)) // 12345678987654322n
var str = '123456789876543212'
console.log(BigInt(str) + BigInt(1)) // 123456789876543213n
var str = '1234567898765432123'
console.log(BigInt(str) + BigInt(1)) // 1234567898765432124n
注意BigInt的数据不能直接和number进行算术运算不然报错
从上面的代码我们可以看到这个时候虽然在数字后面多了一个n,我们可以采用下面方法过滤n
String(BigInt(str)).split('n')[0]
虽然这样做进行数字计算,但是还是无法满足这个字符串转为number,这个最后还是一个字符串,如果是后台对数据兼容还可以,但是要是后台接受必须是数字,那这个还是不满足,那我们就需要第三方插件了,比如BigNumber、decimal等