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

时间:2021-09-24 18:53:25

前言

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

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

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

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

原生封装

  1. /**
  2. **加法函数,用来得到精确的加法结果
  3. **说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
  4. **调用:accAdd(arg1,arg2)
  5. **返回值:arg1加上arg2的精确结果
  6. **/
  7. functionaccAdd(arg1,arg2){
  8. letr1,r2,m
  9. try{
  10. r1=arg1.toString().split('.')[1].length
  11. }catch(e){
  12. r1=0
  13. }
  14. try{
  15. r2=arg2.toString().split('.')[1].length
  16. }catch(e){
  17. r2=0
  18. }
  19. m=Math.pow(10,Math.max(r1,r2))
  20. return(arg1*m+arg2*m)/m
  21. }

  1. /**
  2. **减法函数,用来得到精确的减法结果
  3. **说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
  4. **调用:accSub(arg1,arg2)
  5. **返回值:arg1加上arg2的精确结果
  6. **/
  7. functionaccSub(arg1,arg2){
  8. varr1,r2,m,n;
  9. try{
  10. r1=arg1.toString().split(".")[1].length;
  11. }catch(e){
  12. r1=0;
  13. }
  14. try{
  15. r2=arg2.toString().split(".")[1].length;
  16. }catch(e){
  17. r2=0;
  18. }
  19. m=Math.pow(10,Math.max(r1,r2));//lastmodifybydeeka//动态控制精度长度
  20. n=r1>=r2?r1:r2;
  21. return((arg1*m-arg2*m)/m).toFixed(n);
  22. }

  1. /**
  2. **乘法函数,用来得到精确的乘法结果
  3. **说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
  4. **调用:accMul(arg1,arg2)
  5. **返回值:arg1乘以arg2的精确结果
  6. **/
  7. functionaccMul(arg1,arg2){
  8. letm=0
  9. lets1=arg1.toString()
  10. lets2=arg2.toString()
  11. try{
  12. m+=s1.split('.')[1]?s1.split('.')[1].length:''
  13. }catch(e){}
  14. try{
  15. m+=s2.split('.')[1]?s2.split('.')[1].length:''
  16. }catch(e){}
  17. return(Number(s1.replace('.',''))*Number(s2.replace('.','')))/Math.pow(10,m)
  18. }

  1. /**
  2. **除法函数,用来得到精确的除法结果
  3. **说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
  4. **调用:accDiv(arg1,arg2)
  5. **返回值:arg1除以arg2的精确结果
  6. **/
  7. functionaccDiv(arg1,arg2){
  8. lett1=0
  9. lett2=0
  10. letr1
  11. letr2
  12. try{
  13. t1=arg1.toString().split('.')[1].length
  14. }catch(e){}
  15. try{
  16. t2=arg2.toString().split('.')[1].length
  17. }catch(e){}
  18. r1=Number(arg1.toString().replace('.',''))
  19. r2=Number(arg2.toString().replace('.',''))
  20. return(r1/r2)*Math.pow(10,t2-t1)
  21. }

封装

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

  1. exportconstcalcFn={
  2. add(){
  3. constarg=Array.from(arguments)
  4. returnarg.reduce((total,num)=>{
  5. returnaccAdd(total,num)
  6. })
  7. },
  8. sub(){
  9. constarg=Array.from(arguments)
  10. returnarg.reduce((total,num)=>{
  11. returnaccSub(total,num)
  12. })
  13. },
  14. mul(){
  15. constarg=Array.from(arguments)
  16. returnarg.reduce((total,num)=>{
  17. returnaccMul(total,num)
  18. })
  19. },
  20. divide(){
  21. constarg=Array.from(arguments)
  22. returnarg.reduce((total,num)=>{
  23. returnaccDiv(total,num)
  24. })
  25. }
  26. }

big.js

  • 介绍:任意精度十进制算术的小型、快速、易于使用的库。
  • 特性:目前同类型最小包、无依赖、包大小3 KB、兼容ECMAScript 3+可以说适用于所有浏览器。
  • 官网:GitHub

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

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

安装使用

浏览器

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

Node.js

  1. npminstallbig.js

使用

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

运算符操作函数

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

  • abs,取绝对值。
  • cmp,compare的缩写,即比较函数。
  • div,除法。
  • eq,equal的缩写,即相等比较。
  • gt,大于。
  • gte,小于等于,e表示equal。
  • lt,小于。
  • lte,小于等于,e表示equal。
  • minus,减法。
  • mod,取余。
  • plus,加法。
  • pow,次方。
  • prec,按精度舍入,参数表示整体位数。
  • round,按精度舍入,参数表示小数点后位数。
  • sqrt,开方。
  • times,乘法。
  • toExponential,转化为科学计数法,参数代表精度位数。
  • toFied,补全位数,参数代表小数点后位数。
  • toJSON和toString,转化为字符串。
  • toPrecision,按指定有效位数展示,参数为有效位数。
  • toNumber,转化为JavaScript中number类型。
  • valueOf,包含负号(如果为负数或者-0)的字符串。

封装

  1. importBigfrom'big.js'
  2. exportconstcalcFn={
  3. add(){
  4. constarg=Array.from(arguments)
  5. returnarg.reduce((total,num)=>{
  6. returnnewBig(total).plus(newBig(num))
  7. }).toString()*1
  8. },
  9. sub(){
  10. constarg=Array.from(arguments)
  11. returnarg.reduce((total,num)=>{
  12. returnnewBig(total).minus(newBig(num))
  13. }).toString()*1
  14. },
  15. mul(){
  16. constarg=Array.from(arguments)
  17. returnarg.reduce((total,num)=>{
  18. returnnewBig(total).times(newBig(num))
  19. }).toString()*1
  20. },
  21. divide(){
  22. constarg=Array.from(arguments)
  23. returnarg.reduce((total,num)=>{
  24. returnnewBig(total).div(newBig(num))
  25. }).toString()*1
  26. }
  27. }

使用

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

bignumber.js

  • 介绍:用于任意精度十进制和非十进制算术的 JavaScript 库。
  • 特性:无依赖、包大小8 KB、兼容ECMAScript 3+可以说适用于所有浏览器。
  • 官网:GitHub

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

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

使用方法类似,同上。

decimal.js

  • 介绍:为 JavaScript 提供十进制类型的任意精度数值。
  • 特性:无依赖、包大小12.6 KB、兼容ECMAScript 3+可以说适用于所有浏览器。
  • 官网:GitHub

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

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

使用方法类似,同上。

Math.js

  • 介绍:用 Javascript 编写的简单数学库,可能不维护了。
  • 特性:是一个广泛的 JavaScript 和 Node.js 数学库。它具有灵活的表达式解析器,支持符号计算,带有大量内置函数和常量,并提供了一个集成的解决方案来处理不同的数据类型,如数字、大数、复数、分数、单位和矩阵。功能强大且易于使用。
  • 官网:GitHub

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

总结

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

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

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

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

原文链接:https://mp.weixin.qq.com/s?__biz=Mzg4OTIzMjUyNA==&mid=2247486605&idx=1&sn=216c37f5387299fbacbf50f562e0f03a&chksm=cfee416ef899c8789e4220cba82a6e80df196f82f63d76aa4a79dd78b8df43078926feb8d2db&mpshare=1&