一、Javascript精度问题业务背景JS中 0.1+0.2 = 0.3000000000000004的问题,在很多业务场景里都是一个令人头痛的问题。尤其是在大型的电商企业,货币基金股票行业的网页中,JS四则运算和toFixed精度问题更是让人防不胜防。京东曾经发生过一起线上toFixed精度问题,差点酿成大错:
虽然看起来只有0.01的差别,但是如果消费者用来投诉甚至打官司,这完全可以成为欺诈消费者的理由。原因就是JS的toFixed方法。
二、剖析Javascript精度问题原因
1、超大数和浮点数四则运算精度问题原因在剖析JS精度问题之前,要简单描述下JS计算的方式背景
1、在JS内部所有的计算都是以二进制方式计算的。
2、JS内部无法无限制保存二进制数值的长度。(最长52位)
这两点其实都不难理解。计算机底层都是0和1,当然,计算机也不能保留无限长(无限大)的东西。
知道了这两点,那么自然也就不难理解为什么JS在计算超大的数值的时候,会出现问题了,就好像你永远无法算清宇宙里有多少颗星星一样,计算机也不行。
那么为什么计算很小浮点数的时候也会出问题呢,答案就是,在JS内部,浮点数也是用很长很长的二进制表示的(具体为什么请参考计算机原理)。
在JS的世界里,0.1转换为二进制是0.00011001100110011…你会发现这串数字是无数个0.11在循环..(0.0(0011)(0011)(0011)(0011)…还有无数0011)没办法,在JS里,浮点数就是转化为无穷长的二进制,所以JS只能截取这串二进制的前52位进行二进制的相加,那么下面不用再说了,自然就会出现精度问题。
这也可以解释,为什么JS中整型的数值加减不会出现问题,但是超大数值和浮点数计算会出现问题。
2、toFixed()精度问题toFixed问题其实很简单,就是浏览器的坑。 三、精度问题终极解决方案。 1、四则运算精度问题解决方案 前面提到JS中整型运算是没有问题的,那么把浮点数扩大相应的整数倍,转化成整型在进行计算,计算完缩小为整数倍不就可以解决这个问题了么。 这个方案确实是可行的,然鹅,怎么实施这个方案,却需要琢磨下。 首先第一想到的是乘法,512.06*100就可以转化为 51206了,然鹅。。 有点坑,因为浮点数的计算本身就会有问题,显然想简单的通过乘法来实现整型转化,是不靠谱的,这时候就要考虑自己实现一个万无一失的整型转化方法。 将数字当做字符串,手动的进行小数点移位,这个方案把浮点型转换为整型是完全可行的,接下来就考虑怎么实现的问题。 因为在JS里,超过一定大小的number就会被JS自动转化为科学计算法,所以在考虑把number当成字符串处理时。一定要考虑到这一点。 解决方案的思路就是这样,具体的代码写起来很相对比较复杂,我参考网上的function自己整合了一个npm包,需要的朋友可以自己查看下源码。 总体的思路就是上文介绍的,具体代码实现可以参考源码(如果能顺便点个赞,那最好啦) 2、toFixed解决方案 toFixed的方案相对要好解决,只要有了精度没问题的四则运算,四舍五入只要直接判断下浮点数需要精确位数是否大于5即可,具体的代码实现也可以参考源码。
点击有惊喜
|