手机版

详细讲解JavaScript浮点数运算的精度

时间:2021-08-21 来源:互联网 编辑:宝哥软件园 浏览:

问题描述

在JavaScript中,整数和浮点数都属于数字数据类型,所有数字都存储为64位浮点数,甚至是整数。所以当我们打印像1.00这样的浮点数时,结果是1而不是1.00。在一些特殊的数字表达式中,比如金额,看起来有点扭曲,但至少数值是正确的。然而,致命的是,当浮点数进行数学运算时,您经常会发现一些问题,例如:

//加法==========================//0 . 10 . 2=0.300000000004//0.7 0.1=0.799999999999//0 . 20 . 4=2.22 0 . 1=2.320000000000003//减法=======================//1.5-1.2=0.30000000000000003 100 10=130637763.99999999//0.7 * 180=125.99999999999999//9.7 * 100=969.999999999999//39.7 * 100=3970.000000000005//除法===========================0.3/0.1

似乎不可思议。一个小学生会算,JavaScript不会算的问题?让我们看看真正的原因。

JavaScript中的数字是使用IEEE 754标准的64位双精度浮点数。该规范定义了浮点数的格式。对于内存中64位浮点数的表示,最高的1位是符号位,接下来的11位是指数,剩下的52位是有效数字。具体来说:

位0:符号位,s表示,0表示正数,1表示负数;第1位至第11位:存储索引部分,e表示;第12位到第63位:存储小数部分(即有效数字),f表示,如图:

符号位决定数字是正还是负,指数部分决定数值,小数部分决定数值的精度。根据IEEE 754,默认情况下,有效数字的第一位总是1,这不是存储在64位浮点数中。也就是说,有效数字始终是1的形式。xx.xx,其中xx的部分.xx存储在64位浮点数中,最长可能是52位。因此,JavaScript提供的最大有效位数是53个二进制数字(64位浮点最后52个有效数字的第一个数字为1)。

计算过程

比如在JavaScript中计算0.1 0.2的时候,发生了什么?

首先,十进制的0.1和0.2会被转换成二进制,但是浮点数在用二进制表示时是无限的,例如。

0.1-0.0001100110011001 .(无限)0.2-0.0011001100110011.(无限)IEEE 754标准的64位双精度浮点数的小数部分最多支持53位,所以两者相加后得到的二进制数为:

0.0100110011001100110011001100110011001100110011001100110011001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000因此,算术计算会有误差。

整数的精度

在Javascript中,整数精度也有问题。我们先来看看问题:

console . log(19571992547450991);//=19571992547450990 console . log(19571992547450991===19571992547450992);//=true出于同样的原因,在JavaScript中,number类型被统一视为浮点数,整数被计算为最大的54位(253-1,Number。max _ safe _ integer,9007199254740991)和最小的(-(253-1),数字。min _ safe _ integer,-integer)因此,只要超过这个范围,就会出现精度的问题。

当然,这个问题不仅仅出现在Javascript中。几乎所有的编程语言都采用IEEE-745浮点表示法,任何使用二进制浮点数的编程语言都会有这个问题。但是在其他很多语言中,为了避免准确性的问题,已经对方法进行了封装,而JavaScript是弱类型语言,所以在设计思想上对于浮点数没有严格的数据类型,所以准确性错误的问题就显得尤为突出。

解决办法

上面提到的问题和原因太多了,所以这里有一些解决方法。

classlib

通常这种高精度的计算都要交给后端进行计算和存储,因为后端有成熟的库来解决这种计算问题。前端也有几个不错的类库:

数学. js

Math.js是专门为JavaScript和Node.js提供的一个广泛的数学库,它有一个灵活的表达式解析器,支持符号计算,配备了大量的内置函数和常量,并提供了处理不同数据类型的集成解决方案

如数字、大数(超过安全数的数)、复数、分数、单位和矩阵。功能强大且易于使用。

官方网站:http://mathjs.org/

https://github.com/josdejong/mathjs

decimal.js

为JavaScript提供任意精度的十进制值。

官方网站:http://mikemcl.github.io/decimal.js/

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

big.js

官方网站:http://mikemcl.github.io/big.js

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

这些类库帮助我们解决了很多这样的问题,但通常我们的前端操作只用于表示层,应用程序并不多。因此,在很多情况下,函数能够解决的问题不需要引用类库。

这里有一些更简单的解决方案。

整数表示

至于整数,我们可以通过使用字符串表示来取值或传递值,否则我们将失去精度。

格式化数字、金额、保留小数位数等。

如果你只是格式化数字、金额,保留几个小数等。你可以在这里查看https://www.jb51.net/article/165993.htm

浮点运算

ToFixed()方法

浮点数运算的解法有很多,这里给出一个常见的解法,在判断浮点数运算结果之前,先降低计算结果的精度,因为在精度降低的过程中,总是会自动四舍五入。

toFixed()方法使用定点表示法格式化一个数字,并对结果进行舍入。语法是:

NumObj.toFixed(位数)参数位数表示小数点后的位数;介于0和20之间,包括0和20,实现环境可能支持更大的范围。如果忽略此参数,则默认为0。

返回数值的字符串表示形式,不使用指数表示法,但小数点后有数字。如果需要,该值将被舍入,如果需要,小数部分用0填充,以便小数部分具有指定的位数。如果该值大于1e 21,则该方法只需调用Number.prototype.toString()并返回指数表示格式的字符串。

注意:toFixed()返回数值的字符串表示形式。

详见MDN中的说明,这样我们就可以如下解决精度问题:

ParseFloat((数学表达式)。toFixed(数字)););//toFixed()精度参数必须介于0和20之间。//运行Parsefloat ((1.0-0.9)。tofixed(10))//结果为0.1 Parsefloat ((0.3/0.1)。to fixed(10))//结果是3 Parsefloat(()。toFixed(10)) //结果为970 Parsefloat((2.220.1)。to fixed(10))//结果是2.32。在旧版本的IE浏览器(IE 6,7,8)中,tofixed()方法的返回值不一定准确。所以这种方法以前很少使用。因此,在线搜索结果大多是以下方法。

还有一些其他的解决方案。简单来说,浮点数需要转换成字符串,字符串分为整数部分和小数部分。小数部分然后被转换成整数,然后在计算结果后被转换成浮点数。这个过程有点复杂.在线查找:

加法函数

/* * * *加法函数,用于得到准确的加法结果* *说明:javascript的加法结果会有错误,两个浮点数相加会很明显。该函数返回更精确的加法结果。* *调用:accAdd(arg1,arg2) **返回值:arg1加arg2的精确结果* */函数accadd (arg1,arg2) {varr1,R2,m,c;尝试{ r1=arg1.toString()。拆分('.')[1].长度;} catch(e){ R1=0;}尝试{ r2=arg2.toString()。拆分('.')[1].长度;} catch(e){ R2=0;} c=Math . ABS(R1-R2);m=Math.pow(10,Math.max(r1,R2));if(c ^ 0){ var cm=math . pow(10,c);if(R1 R2){ arg 1=NumBer(arg 1 . ToString()。替换('.', ''));arg2=Number(arg2.toString()。替换('.',' '))* cm} else { arg 1=Number(arg 1 . tostring()。替换('.',' '))* cmarg2=Number(arg2.toString()。替换('.', ''));} } else { arg 1=Number(arg 1 . tostring()。替换('.', ''));arg2=Number(arg2.toString()。替换('.', ''));}返回(arg 1 arg 2)/m;}//在Number类型中增加一个add方法,调用起来更方便。number . prototype . add=function(arg){ return accAdd(arg,this);};减法函数

/* * * *减法函数,用来得到精确的减法结果* *说明:javascript减法结果会有误差,两个浮点数相减会很明显。该函数返回更精确的减法结果。* *调用:accSub(arg1,arg2) **返回值:arg1加arg2的精确结果* */函数accsub (arg1,arg2) {varr1,R2,m,n;尝试{ r1=arg1.toString()。拆分('.')[1].长度;} catch(e){ R1=0;}尝试{ r2=arg2.toString()。拆分('.')[1].长度;} catch(e){ R2=0;} m=Math.pow(10,Math.max(r1,R2));//最后由deeka修改//动态控制精度长度n=(r1=r2)?r1 : r2返回((arg1 * m - arg2 * m)/m)。toFixed(n);}//在Number类型中加入mul方法,调用起来更方便。number . prototype . sub=function(arg){ return accMul(arg,this);};乘法函数

/* * * *乘法函数,用来得到准确的乘法结果* *说明:javascript的乘法结果会有误差,两个浮点数相乘会很明显。该函数返回更精确的乘法结果。* *调用:accMul(arg1,arg2) **返回值:arg1乘以arg2的精确结果* */函数accmul (arg1,arg2) {var m=0,S1=arg1.tostring(),S2=arg 2 . tostring();尝试{ m=s1.split(' . ')[1].长度;} catch (e) { } try { m=s2.split(' . ')[1].长度;} catch(e){ } return Number(S1 . replace(' . ',' '))*编号(s2.replace(' . ',' '))/Math.pow(10,m);}//在Number类型中加入mul方法,调用起来更方便。number . prototype . mul=function(arg){ return accMul(arg,this);};除法函数

/* * * *除法函数,用来得到精确的除法结果* *说明:javascript除法结果会有错误,两个浮点数相除时会很明显。该函数返回更精确的除法结果。* *调用:accDiv(arg1,arg2) **返回值:arg1除以arg2的精确结果* */函数accdiv (arg1,arg2) {vart1=0,T2=0,R1,R2;尝试{ t1=arg1.toString()。拆分('.')[1].长度;} catch (e) { }尝试{ t2=arg2.toString()。拆分('.')[1].长度;}用(Math) { r1=Number(arg1.toString)()捕获(e) { }。替换('.', ''));r2=Number(arg2.toString()。替换('.', ''));返回(r1/r2) *幂(10,T2-t1);} }//在Number类型中增加一个div方法,调用起来更方便。number . prototype . div=function(arg){ return accDiv(this,arg);};以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。

版权声明:详细讲解JavaScript浮点数运算的精度是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。