文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

我是这样解决JavaScrip 加减乘除精度问题

2024-12-02 18:07

关注

众所周知的 JavaScript 二进制精度问题,浮点数的计算精度会存在缺失问题。最经典的例子就是为什么0.1+0.2 !== 0.3

一句话概括就是:ECMAScript规范定义Number的类型遵循了IEEE754-2008中的64位浮点数规则定义的小数后的有效位数至多为52位导致计算出现精度丢失问题!

不过网上已经有很多专门的类库可以解决这个问题。

原生封装

 

  1.  
  2. function accAdd(arg1, arg2) { 
  3.     let r1, r2, m 
  4.     try { 
  5.         r1 = arg1.toString().split('.')[1].length 
  6.     } catch (e) { 
  7.         r1 = 0 
  8.     } 
  9.     try { 
  10.         r2 = arg2.toString().split('.')[1].length 
  11.     } catch (e) { 
  12.         r2 = 0 
  13.     } 
  14.     m = Math.pow(10, Math.max(r1, r2)) 
  15.     return (arg1 * m + arg2 * m) / m 

 

  1.  
  2. function accSub(arg1, arg2) { 
  3.   var r1, r2, m, n; 
  4.   try { 
  5.     r1 = arg1.toString().split(".")[1].length; 
  6.   } catch (e) { 
  7.     r1 = 0; 
  8.   } 
  9.   try { 
  10.     r2 = arg2.toString().split(".")[1].length; 
  11.   } catch (e) { 
  12.     r2 = 0; 
  13.   } 
  14.   m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度 
  15.   n = r1 >= r2 ? r1 : r2; 
  16.   return ((arg1 * m - arg2 * m) / m).toFixed(n); 

 

  1.  
  2.   
  3. function accMul(arg1, arg2) { 
  4.     let m = 0 
  5.     let s1 = arg1.toString() 
  6.     let s2 = arg2.toString() 
  7.     try { 
  8.         m += s1.split('.')[1] ? s1.split('.')[1].length : '' 
  9.     } catch (e) {} 
  10.     try { 
  11.         m += s2.split('.')[1] ? s2.split('.')[1].length : '' 
  12.     } catch (e) {} 
  13.     return (Number(s1.replace('.''')) * Number(s2.replace('.'''))) / Math.pow(10, m) 

 

  1.  
  2. function accDiv(arg1, arg2) { 
  3.     let t1 = 0 
  4.     let t2 = 0 
  5.     let r1 
  6.     let r2 
  7.     try { 
  8.         t1 = arg1.toString().split('.')[1].length 
  9.     } catch (e) {} 
  10.     try { 
  11.         t2 = arg2.toString().split('.')[1].length 
  12.     } catch (e) {} 
  13.     r1 = Number(arg1.toString().replace('.''')) 
  14.     r2 = Number(arg2.toString().replace('.''')) 
  15.     return (r1 / r2) * Math.pow(10, t2 - t1) 

封装

定义一个函数来调用加减乘除方法,这样做有个好处,用到地方调用加减乘除方法一致,假设某个方法后面发现那个库更好用或者某个平台不兼容、算法不太严谨、扩展新的功能等等,我们只要维护这个函数就行,不用在考虑项目中某个组件单独引用,没有按照这个规范因为这次维护引发的新问题。

 

  1. export const calcFn = { 
  2.     add() { 
  3.         const arg = Array.from(arguments) 
  4.         return arg.reduce((total, num) => { 
  5.             return accAdd(total, num) 
  6.         }) 
  7.     }, 
  8.     sub() { 
  9.         const arg = Array.from(arguments) 
  10.         return arg.reduce((total, num) => { 
  11.             return accSub(total, num) 
  12.         }) 
  13.     }, 
  14.     mul() { 
  15.         const arg = Array.from(arguments) 
  16.         return arg.reduce((total, num) => { 
  17.             return accMul(total, num) 
  18.         }) 
  19.     }, 
  20.     divide() { 
  21.         const arg = Array.from(arguments) 
  22.         return arg.reduce((total, num) => { 
  23.             return accDiv(total, num) 
  24.         }) 
  25.     } 

big.js

 

 

 

 

https://github.com/MikeMcl/big.js/

安装使用

浏览器

 

 

  1. 'https://cdn.jsdelivr.net/npm/big.js@6.1.1/big.min.js'

 

Node.js

  1. npm install big.js 

使用

 

  1. x = new Big(0.1) 
  2. y = new Big(0.2)                  
  3. z = new Big(0.3) 
  4. x.plus(y).eq(z)     // true 

运算符操作函数

以下big.js目前支持运算符操作函数。

封装

 

  1. import Big from 'big.js' 
  2.  
  3. export const calcFn = { 
  4.     add() { 
  5.         const arg = Array.from(arguments) 
  6.         return arg.reduce((total, num) => { 
  7.             return new Big(total).plus(new Big(num)) 
  8.         }).toString() * 1 
  9.     }, 
  10.     sub() { 
  11.         const arg = Array.from(arguments) 
  12.         return arg.reduce((total, num) => { 
  13.             return new Big(total).minus(new Big(num)) 
  14.         }).toString() * 1 
  15.     }, 
  16.     mul() { 
  17.         const arg = Array.from(arguments) 
  18.         return arg.reduce((total, num) => { 
  19.             return new Big(total).times(new Big(num)) 
  20.         }).toString() * 1 
  21.     }, 
  22.     divide() { 
  23.         const arg = Array.from(arguments) 
  24.         return arg.reduce((total, num) => { 
  25.             return new Big(total).div(new Big(num)) 
  26.         }).toString() * 1 
  27.     } 

使用

 

  1. calcFn.add(0.1, 0.2) !== 0.3 // false 

bignumber.js

 

 

 

 

https://github.com/MikeMcl/bignumber.js

使用方法类似,同上。

decimal.js

 

 

 

 

https://github.com/MikeMcl/decimal.js

使用方法类似,同上。

Math.js

 

 

 

 

总结

big.js适用于大部分十进制算术应用程序,因为不接受NaN或Infinity作为合法值。而且不支持其他基数的值。如果项目中没有非十进制算术这非常适合用,而且关键是包足过小,哈哈自己造的轮子后面还是觉得库比较香哈。

bignumber.js可能更适合金融应用,因为除非使用涉及除法的运算,否则用户无需担心会丢失精度。

decimal.js可能更适合更科学的应用程序,因为它可以更有效地处理非常小的或大的值。例如,它没有bignumber.js的限制,当将一个小指数的值与一个大指数的值相加时,bignumber.js会尝试执行全精度运算,这可能会导致操作不可行。

如上所述,decimal.js还支持非整数幂,并增加了三角函数和exp,ln和log方法。这些添加使decimal.js明显大于bignumber.js。

 

来源:前端有道内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯