美文网首页前端知识点
js浮点数精度计算问题

js浮点数精度计算问题

作者: AmazRan | 来源:发表于2019-10-20 17:23 被阅读0次

    前言

    项目中,总是不可避免会出现js的浮点数精度计算问题。
    抛出经典问题:为什么0.1===0.10.1+0.2!==0.3


    出现问题的原因

    根源要追溯到JavaScript的浮点数的存储上。和其它语言如Java和Python不同,JavaScript中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用64位固定长度来表示,也就是标准的 double 双精度浮点数(相关的还有float 32位单精度)。
    这样的存储结构优点是可以归一化处理整数和小数,节省存储空间。64位比特又可分为三个部分:

    • 符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数
    • 指数位E:中间的 11 位存储指数(exponent),用来表示次方数
    • 尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零


      图片.png

    在了解了存储的原理之后可能还不太理解。通过案例应该可以更加具体一点。
    在执行计算0.1+0.2的过程中发生了什么?
    因为计算机内部只能通过二级制处理数据,0.1和0.2会先被转换成二进制,但是由于浮点数用二进制表示时是无穷的,因此会被转换为:

    0.1 -> 0.0001 1001 1001 1001...(1001循环)
    0.2 -> 0.0011 0011 0011 0011...(0011循环)
    

    相加之后就是0.0100 1100 ...(1100循环)
    前面提到了,JavaScript提供的有效数字最长为53个二进制位(64位浮点的后52位+有效数字第一位的1)。最后的 52 位尾数超出部分发生了截断,因此,0.1+0.2的计算结果在截断后,二进制转换回十进制,得到的结果是0.30000000000000004


    整数精度问题

    前面提到了,既然小数存在,那整数也存在着上限。
    js的Number自带存储了最大/最小安全整数
    Number.MAX_SAFE_INTEGER === 9007199254740991(2^53 - 1)
    Number.MIN_SAFE_INTEGER === -9007199254740991-(2^53 - 1)

    至于为什么不是52次方-1。对于二进制来说, 小数点前保留一位, 规格化后始终是 1.***, 节省了 1 bit,这个 1 并不需要保存,所以可以多展示1位数,即2^53-1。

    那么超出之后进行运算会发生什么就不用再解释了,原理同浮点数是一样的。


    解决方案

    除了自己造轮子的运算中进位,更推荐使用Math.js、decimal.js、big.js等类库,其中有完善的解决方案,具体想了解实现原理可以移步官方git直接查看函数方法。

    不过通常不需要引入整个库,只需要其中的几个方法就适用了,例如常用的保留小数后几位(基于parseFloat和tofixed去改造……)


    心得体会

    前端的浮点数问题并不会太复杂,因为涉及到业务数据的计算通常都是交给后端完成,但是精确位数尤其是金额的展示还是不能出错。虽然涉及到计算机组成原理的知识,其实还是很容易弄懂的。


    参考

    JS中浮点数精度问题

    相关文章

      网友评论

        本文标题:js浮点数精度计算问题

        本文链接:https://www.haomeiwen.com/subject/mohamctx.html