js数值类型计算一直有一个坑,就是关于计算精度的问题。在js中计算 0.1+0.2、0.3-0.1等运算时,会出现很惊人的一大串数值,相信很多人都遇到过
0.1+0.2 //0.300000000000000040.3-0.1 //0.19999999999999998复制代码
关于产生这个问题的原因,网上已经有很多说明了,大体就是js浮点类型精度的问题导致的,因此在计算的时候我们通常会采取一些方法,截取所需要的值。
回到正题,这次遇到的坑也是精度问题,但是不是加减法而是乘法,一个浮点数乘以10,结果也是惊人的长————————
0.68*10 //6.800000000000001复制代码
可能很多人遇到过乘法精度问题,但是我确实是第一次遇到这种问题。问题产生的背景是,后端返回一个浮点数m,前端在此基础上乘10,转换为“m折”字样展现在页面上。需求很简单,但是却有一个隐藏的坑。
其实要解决这个坑并不难,网上也有很多办法,此处列举几个:
(1)toFixed方法
浮点数*10然后调用toFixed方法可以解决此问题,但是也有一个问题,就是会存在四舍五入的问题,如果要求准确的展示后台返回的数值,这个方法就会有缺陷。
(0.68*10).toFixed(2) //此时没问题,结果为6.80(0.0686*10).toFixed(2) //小数位数多的时候就会有四舍五入问题,此时结果为0.69复制代码
(2)将浮点数乘以10的倍数,再除以相应的10的倍数
这个方法也很常用,就是将浮点数转化为整数,在除以相应的倍数转换为浮点数。这个方法基本可以覆盖很大部分问题,但是一种方法不试用,就是浮点数的位数不确定的时候,无法准确判断乘以10的准确倍数。
0.68*1000/100 //此时结果正常,结果为6.8假如返回的浮点数不确定位数,但是依旧按乘1000/100方式计算0.00000068*1000/100 //结果又很给力了, 结果为0.000006799999999999999复制代码
(3)利用Math相关方法转换
此方法主要是利用Math.round和Math.pow方法对数据进行处理,借用网上的一个方法
function myFixed(a, b) { return Math.round(a * Math.pow(10, b)) / Math.pow(10, b);}console.log(0.68*10); //6.800000000000001console.log(myFixed(0.68*10, 2)); //6.8复制代码
(4)字符串转换方法
最后说一下我们填坑的方法,因为此处对数值的应用只是将其转化为折扣数值,可以说是不需要进行运算处理的,因此采用了一个直截了当的字符串截取的方法,直接上代码:
//此方法考虑数值都是大于等于0的数值,并且对0和大于1的数值进行了展示(理论上折扣券不会出现0和大于1的情况)function discounts(discountNumber){ if (!discountNumber && discountNumber !== 0) { return } const expPoint = /\./ let countString = String(discountNumber) let discountString = '' if(countString < 1 && expPoint.test(countString)){ let discountArr = countString.split('.')[1].split('') if(discountArr.length === 1){ discountString = discountArr[0] }else{ let discountStringFirst = discountArr[0] let discountStringSecond = discountArr.slice(1).join('') discountString = discountStringFirst + '.' + discountStringSecond } }else{ discountString = countString * 10 } return discountString}discounts(0) //0discounts(0.3) //3discounts(0.68) //6.8discounts(0.9) //9discounts(1) //10discounts(1.02) //10.2复制代码
到此问题得到了解决,并且基本能覆盖到所有正常情况。
最后要说的是,关于数值计算精度问题在开发中一定要小心,稍有不慎就会掉入超长浮点数的坑里,在此也是记录并且告诉下自己,避免后面再次入坑。