JS浮点数计算失精

JS浮点数计算失精

问题:

二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,四舍五入到最接近的可表示的实数。

执行一系列的运算,随着舍入误差的积累,运算结果会越来越不精确。舍入也会使我们通常所期望的算术运算定律产生一些出人意料的偏差。例如,实数满足结合律,这意味着,对于任意的实数x,y,z,总是满足(x + y) + z = x + (y + z)。

这种计算结果可以胜任大多数的计算任务:浮点数失去精度通常在比较两个值是否相等时才会出现。

下文尝试分析一下原因以及解决方法。

float

二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,四舍五入到最接近的可表示的实数。

执行一系列的运算,随着舍入误差的积累,运算结果会越来越不精确。舍入也会使我们通常所期望的算术运算定律产生一些出人意料的偏差。例如,实数满足结合律,这意味着,对于任意的实数x,y,z,总是满足(x + y) + z = x + (y + z)。

这种计算结果可以胜任大多数的计算任务:浮点数失去精度通常在比较两个值是否相等时才会出现。

下文尝试分析一下原因以及解决方法。

在 ES6 的新标准中提出了像 Int8Array这样新的数据类型,在 ES6 之前数字类型是不区分什么 Int,Float,Double,Decimal 的。

实际上,JavaScript的数字类型的本质就是一个基于 IEEE 754 标准的双精度 64 位的浮点数。按照标准,它的数据结构如图示这样:由1位符号位,11位指数部分以及52位尾数部分构成。

general double float number

由此二进制运算只能表示一组有限的数字,而不能表示所有的实数集,不精确。甚至一些看起来最简单的算术运算都会产生不正确的结果。0.1+0.2=0.300000000000004

解决的思路有

  1. parseInt,parseFloat

            var a = "0.11"; 
            var b = "0.2801"; 
            var c = "1.002";
    

    a,b,c相加本来为1.3921,但sum1得出的结果为:1.3921000000000001,这不是想要的结果,特别在计算金钱的时候,不需要如此精确的精度。可以使用toFixed(n)方法修正后(n是小数后精确的位数)。
    例如:

        parseFloat(1.392143).toFixed(2)=1.39
    
  2. 位运算符:

    这也是一种思路,但是并不推介。原因是按照ES标准,两个需要运算的值会被先转为有符号的32位整型。所以超过32位的整数会被截断,而小数部分则会被直接舍弃。

    ps,我们在什么情况下需要用到位操作符?使用左移来代替 2 的幂的乘法?而且对一个浮点数进行左移操作是否比直接乘 2 来得效率高,这也是个值得商榷的问题。

    用来表示标志位呢?首先,现在的内存大小已经不值得我们用精简几个变量来减少存储空间了;其次呢,使用标志位也会使得代码的可读性大大下降。

  3. 计算出小数点后位数的大的一个,乘法消去小数点做整数运算后做除法

        (arg1 * m - arg2 * m) / m
    
  4. 转化为字符串处理

    Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
    
  5. 使用三方JS插件,如:
    big.js,bignumber.js,decimal.js等等,其差别以及优缺点请移步https://github.com/MikeMcl


    先这样,等有了更多体会,再回来呦。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值