宝哥软件园

十进制加减乘除精度损失的javascript解决方案

编辑:宝哥软件园 来源:互联网 时间:2021-10-28

原因是:js按照二进制处理小数的加、减、乘、除,在arg1的基础上扩展或逆扩展arg2的精度,所以会出现以下情况。

javascript(js)的小数点加减乘除是一个js bug,比如0.3 * 1=0.299999999等。这里有四个js算法可以完美的找到对应的精度。

函数accDiv(arg1,arg2){ var t1=0,t2=0,r1,R2;尝试{t1=arg1.toString()。拆分('.')[1].length}catch(e){}尝试{t2=arg2.toString()。拆分('.')[1].length } catch(e){ } with(Math){ R1=Number(arg 1 . tostring)()。替换('.',' '))r2=Number(arg2.toString()。替换('.',''))返回accMul((r1/r2),pow(10,T2-t1));} }//乘法函数accmul (arg1,arg2) {var m=0,S1=arg1。tostring(),S2=arg2。tostring();尝试{m=s1.split(' . ')[1].length}catch(e){}尝试{m=s2.split(' . ')[1].长度{ catch(e)} { return Number(S1 . replace(' . ',' '))*编号(s2.replace(' . ',)/Math.pow(10,m)} //加法函数accadd (arg1,arg2) {varr1,R2,m;尝试{r1=arg1.toString()。拆分('.')[1].length}catch(e){r1=0}尝试{r2=arg2.toString()。拆分('.')[1].length } catch(e){ r2=0 } m=math . pow(10,Math.max(r1,R2)) return (arg1*m arg2*m)/m} //减法函数subtr (arg1,arg2) {varr1,R2,m,n;尝试{r1=arg1.toString()。拆分('.')[1].length}catch(e){r1=0}尝试{r2=arg2.toString()。拆分('.')[1].length } catch(e){ R2=0 } m=math . pow(10,Math.max(r1,R2));n=(r1=r2)?r1:r2返回((arg1*m-arg2*m)/m)。toFixed(n);}我们来详细分析一下JavaScript中数字精度的损失。

1.JS数字精度损失的几个典型问题?

1.添加两个简单的浮点数。

0.1 0.2 !=0.3 //trueFirebug

这真的不是Firebug的问题。可以带着警惕试试(哈哈,开玩笑的)。

看看Java的计算结果。

再看看Python。

2.大整数运算。

9999999999999999==10000000000000001 //?

萤火虫

16位数和17位数是偶数是不合理的。

另一个例子是

var x=9007199254740992x 1==x /?

看看结果

三观再次被颠覆。

3.toFixed不会被舍入(Chrome)。

1.335.toFixed(2) //1.33Firebug

Chrome和其他浏览器之间存在在线价格不一致,这是由toFixed兼容性问题引起的。

二、JS数字丢失准确性的原因。

计算机二进制实现和位限制。有些数字不能用有限的方式表达。就像有些无理数不能用有限项表示一样,比如pi 3.1415926,pi 1.3333等。按照IEEE 754标准,JS采用双精度,占用64位。数字

意义

例如,1位用于符号,11位用于指数,52位用于尾数浮点数。

0.1 0.0001 1001 1001 .(1001无限循环)0.2 0.0011 0011 0011.(0011无限循环)此时只能模仿十进制四舍五入,但二进制只有0和1两种,所以就变成了0和1。这就是为什么计算机中的一些浮点数有错误而失去准确性的根本原因。

大整数的精度损失和浮点数的精度损失本质上是一样的,最大尾数位是52位,所以在JS中可以精确表示的最大整数是Math.pow(2,53),十进制是9007199254740992。

大于9007199254740992可能会失去准确性。

007199254740992 100000000000 .000//总共53 09007199254740992 1 10000000000000.001//中间52 09007199254740992 2 2 1000000000000。

9007199254740992 1 //缺失900719925474092 2//未缺失900719925474092 3//缺失900719925474092 4//未缺失结果如图所示。

从上面可以知道,在计算机的二进制表示中,看似有限的数是无限的。由于存储位数的限制,出现了“丢弃”,出现了精度损失。

要进行更深入的分析,你可以阅读这篇论文(又长又臭):每个计算机科学家应该知道的关于浮点运算的知识。

第三,解决方案

对于整数,前端出现问题的概率可能比较低。毕竟需要用到很大整数的业务很少,只要运算结果不超过Math.pow(2,53)就不会丢失精度。

对于小数来说,还是有很多前端问题的,尤其是一些涉及金额等数据的电商网站。解决方法:将十进制数转化为整数(乘以倍数),再还原成原来的倍数(倍数除外)。

//0.1 0.2(0.1 * 10 0.2 * 10)/10==0.3//true下面是我写的一个对象,它掩盖了十进制加减乘除丢失的精度。当然,转换后的整数还是不能超过9007199254740992。

/* * * float tobj包括加减乘除四种方法,可以保证浮点计算不丢失精度。* *我们知道计算机编程语言中的浮点计算会有精度损失(或舍入误差)的问题。根本原因是由于二进制和实现数字的限制,有些数字无法用有限的方式表示。*以下是对应十进制分数* 0.1 0.0001 1001 1001的二进制表示.(1001无限循环)* 0.2 0.0011 0011 0011.(0011无限循环)*计算机中每种数据类型的存储宽度有限,例如JavaScript。缺少的部分是失去精度的部分。* * * *方法* * *加/减/多/除* * * * explame * * * 0 . 10 . 2==0.300000000000004(0.00000000004以上)* 0.2 0.4==0.6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000添加(0。0.2)0.3 * float BoJ .乘法(19.9,100)1990 * */var float BoJ=function(){/* *判断obj是否为整数*/function是否为整数(obj) {return math。floor(obj)==obj }/。例如,3.14 314,倍数为100 * @ param float num { num }十进制* @ return { object } * { times :100,num : 314 } */function to integer(float num){ var ret={ times : 1,num : 0 0 } if(Isinteger(float num)){ ret . num=float num return } var strfi=float num ' ' var dot pos=strfi。)var len=strfi.substr(dotPos 1)。lengthvar times=Math.pow(10,len)var intNum=parsent(float num *乘以0.5,10) ret。times=timesret。num=int num ret }/* *核心方法,实现加减乘除保证精度不损失*思路:将小数放大成整数(乘),进行算术运算。然后减少到小数(除)** @param a {number}操作数1* @param b {number}操作数2* @param digits {number}精度,保留的小数点数量,如2,保留为两位小数* @param op {string}运算类型。有加减乘除(加减/乘/除)* */函数运算(a,b,数字,op){ var O1=to integer(a)var O2=to integer(b)var n1=O1。N2=O2。num var t1=O1。T2时间=O2。T1 :t2var结果=null switch(op){ case ' add ' : if(t1==T2){//两个小数位是相同的结果=n1 n2}否则if(t1 T2){///。O1小数位大于O2结果=N1N2 * (t1/t2)}否则{//O1小数位小于O2结果=N1 * (t2/t1) n2}返回结果/maxcase '减法' :IF (t1===t2)。{ result=n1-N2 } else if(t1 T2){ result=n1-N2 *(t1/T2)} else { result=n1 *(T2/t1)-N2 } return result/max case ' multiply ' : result=(n1 * N2)/(t1 * T2)return result case ' divide ' : result=(n1/N2)*(T2/t1)return result } }//加减乘除函数的四个接口相加(a,b,数字){return operation (a,b,数字,' add')} 减法')}函数乘法(a,b,数字){返回运算(a,b,数字,' '乘法')}函数除法(a,b,数字){返回运算(a,b,数字,' '除法')}//exportsreturn {add: add,减法:减法,multiply:乘法,divide:除法} }(); 修复方法如下。

//toFixed修复函数tofixed (num,s) {var times=math.pow (10,s)var des=num * times 0.5 des=par sent(des,10)/times返回des''}。

更多资讯
游戏推荐
更多+