1.Javascript 精度问题的产生
精度问题其实是一个"共性"问题
日常生活中的"十进制"
- 精度问题的产生:使用"十进制除法"无法除尽的时候就会产生精度问题,例如1/3无法在十进制中除尽。
- 精度问题的描述:那我们表达这种无法除尽的问题是用0.3(3循环)来描述。
计算机中的"二级制"
-
精度问题的产生:二进制中在无法乘尽的时候会产生精度问题。
-
精度问题的描述 :计算机由于存储空间限制,无法像十进制那样使用循环表示,因此只能有限的描述数字精度,目前描述方式主要采用四种"浮点型":单精度32位,延伸单精度43位、双精度64位、延伸双精度90位。尽管储存位数很多,但毕竟是有限的。这种 "有限性 " 就导致了java、c++、javascript等计算机语言中的"精度丢失"。
二进制转化:
二进制整数:除法。"二进制"很少存在无法除尽的时候,因为除以二余数要不是0、1,而我们需要的就是0、1。
二进制小数:乘法。经常存在无法乘进的时候,因为小数位乘以2后,数值截整,剩下小数继续乘以2。0
2.Javascript中的精度存储原因
js采用的是浮点运算标准,而IEEE规定的浮点数值运算标准主要有四种方式来保证精度:单精度(32位)、延伸单精度(43位)、双精度(64位)、延伸双精度(80位)。其中js采用的是双精度64位,其中小数部分最多支持53位的存储,如果超出会被截断。
3.工作中哪些场景可能会遇到js精度问题
场景1 :小数循环引起的精度问题 :(小数运算)
case1:比如我们在控制台输入:1.15 * 100 ,那么展示的不是115而是114.999999999999。如果我们向下取整Math.floor,再除以100的话,那么值就变成了1.14,少了0.01。在下付款单的时候,我们经常会遇到这样会那样的乘法除法运算,很可能因为类似情况出现精度丢失问题。
case2:
js
0.1 + 0.2 != 0.3 // True
因为先把0.1、0.2的二进制分别算出来,结果不等于0.3的二进制表示。
场景2 :大数过大引起的精度问题 - 前后端大数据值id传递:前端获取到的roomid
:10976458979374928
, 后端返回的:roomid
:10976458979374929
。并且前端在network中看到的都是10976458979374928
。
这个时候需要先判断是不是缓存的问题,排除缓存问题后,可以考虑是否为精度问题。
那为什么java中可以正常存储呢?因为js中Number类型的数字范围是:-2^53 + 1 到 2^53 - 1
,而java中的取值范围是-2^63 + 1 到 2^63 - 1
,比js要大很多,所以在java中没有出现这个问题。
场景3:toFixed(n)API截取小数位不准确。 问题原因:toFix不是按照银行家的四舍五入规则进行截取的, 而是有自己的一套四舍六入五考虑(五后任有数就进一,五后无数字看前面,五前为奇数应舍去,五前为偶要进一),那么当我们截取两位的时候,并且第二位小数小于5,第三位小数大于5,那么就会导致不会向前进一位。此外返回的类型是字符串类型也需要额外转换。
js
0.615.toFixed(2) // 返回字符串'0.61'而不是'0.62'
1.35.toFixed(1) // '1.4' 正确
1.335.toFixed(2) // '1.33' 错误
1.3335.toFixed(3) // '1.333' 错误
1.33335.toFixed(4) // '1.3334' 正确
1.333335.toFixed(5) // '1.33333' 错误
1.3333335.toFixed(6) // '1.333333' 错误
怎么判断一个Number类型数字是否在允许的精度范围内呢?
在js中Number类型的数字范围是:-2^53 + 1 到 2^53 - 1
ES6中引入了Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER这两个常量来表示上下限范围。 Number.isSafeInteger()
用来判断一个整数是否落在这个范围之内。
4.怎么解决或者避免js精度问题
场景1:小数前端运算导致的精度丢失
场景2:小数位截取toFixed(2)-API导致的精度丢失
场景3:大数前端运算导致的精度丢失
场景4:后端id传大数,前端回显精度丢失
如果是存储位置不够导致的,可以通过第三方库decimal.js使用