JS中 0.1+0.2 = 0.3000000000000004的问题,在不少业务场景里都是一个使人头痛的问题。尤为是在大型的电商企业,货币基金股票行业的网页中,JS四则运算和toFixed精度问题更是让人防不胜防。 京东曾经发生过一块儿线上toFixed精度问题,差点酿成大错: html
在剖析JS精度问题以前,要简单描述下JS计算的方式背景git
一、在JS内部全部的计算都是以二进制方式计算的。github
二、JS内部没法无限制保存二进制数值的长度。(最长52位)算法
这两点其实都不难理解。计算机底层都是0和1,固然,计算机也不能保留无限长(无限大)的东西。chrome
知道了这两点,那么天然也就不难理解为何JS在计算超大的数值的时候,会出现问题了,就好像你永远没法算清宇宙里有多少颗星星同样,计算机也不行。npm
那么为何计算很小浮点数的时候也会出问题呢,答案就是,在JS内部,浮点数也是用很长很长的二进制表示的(具体为何请参考计算机原理)。浏览器
在JS的世界里,0.1转换为二进制是0.00011001100110011…你会发现这串数字是无数个0.11在循环..(0.0(0011)(0011)(0011)(0011)…还有无数0011)没办法,在JS里,浮点数就是转化为无穷长的二进制,因此JS只能截取这串二进制的前52位进行二进制的相加,那么下面不用再说了,天然就会出现精度问题。网络
这也能够解释,为何JS中整型的数值加减不会出现问题,可是超大数值和浮点数计算会出现问题。oracle
toFixed问题其实很简单,就是浏览器的坑。 es5
能够看到,在IE和chrome两个浏览器下面,一个简单的四舍五入,显示的结果彻底不一样。就是由于不一样的浏览器厂商对于四舍五入没有统一的标准,就让咱们这些开发人员踩坑。。 在IE里,toFixed就是采用常规的四舍五入方法,然鹅,在chrome倒是采用金融界的更为精确的四舍六入五成双方法,虽然这个方法更为科学,更为精确,可是却毕竟大多数人不是金融学家也不是科学家,仍是四舍五入更容易被常人所接受。。。
更正:
在toFixed精度问题上直接参考了大部分网络解释,欠缺实践,没有深刻考究,上面四舍六入五成双的方法经反复验证实际上是错误的(上图就打脸了。。),再次查阅一些资料后,通过官方查证toFixed 原生API以下:
这个方法通过10几回toFixed验证后发现都是和结果相吻合的,应该是正确的问题根源所在。
(PS:该错误对解决方案无影响)
前面提到JS中整型运算是没有问题的,那么把浮点数扩大相应的整数倍,转化成整型在进行计算,计算完缩小为整数倍不就能够解决这个问题了么。 这个方案确实是可行的,然鹅,怎么实施这个方案,却须要琢磨下。 首先第一想到的是乘法,512.06*100就能够转化为 51206了,然鹅。。
解决方案的思路就是这样,具体的代码写起来很相对比较复杂,我参考网上的function本身整合了一个npm包,须要的朋友能够本身查看下源码。 整体的思路就是上文介绍的,具体代码实现能够参考源码(若是能顺便点个赞,那最好啦)
toFixed的方案相对要好解决,只要有了精度没问题的四则运算,四舍五入只要直接判断下浮点数须要精确位数是否大于5便可,具体的代码实现也能够参考源码。
解决方案核心的点就在于用字符串方式来解决小数点精度问题,虽然实现上有点复杂,须要考虑的状况比较多,可是确实是最稳妥的作法。若是有须要高精度业务场景的小伙伴能够直接install我封装的npm包,就能够直接用啦,教程参考github哦!
最后,若是这篇文章对你有点点帮助的话,欢迎动动鼠标点个赞哦!
(纯属我的手动打字,有手误或者技术错误的地方,确定多多指正!)