我们前端在做计算过程中,特别时小数的计算,都会遇到意想不到的结果,通常我们在价格的计算中遇到很多问题,这是由于 JS 中的 Number 类型是一个浮点类型。
在 chrome 中的运算结果
1 0.28 * 100 // 28.000000000000004 2 0.29 * 100 // 28.999999999999996 3 0.55 * 100 // 55.00000000000001 4 0.56 * 100 // 56.00000000000001 5 0.57 * 100 // 56.99999999999999 6 0.58 * 100 // 57.99999999999999 7 0.59 * 100 // 59 8 0.60 * 100 // 60
Nubmer 类型
我们先看看 JS 中的 Number 类型。它并不像其它的语言有不同的数字类型,例如:整型、长整型、浮点类型等等,定义不同的数字类型存储的空间也不一样。然而 JS 中的 Number 类型都是浮点类型并且存储空间为 8 数节(byte)(8*8 bit位)。其中Number 类型值的整数最多15位,小数最多17位。
精度问题
JS 中的 Number 是一个双精度浮点类型,双精度浮点类型在 64 位存储空间中的内容如下:
todo:
| 1位 | 11位 |52位 | |--------------:| -----------:|------:| | 符号位 | 指数位 | 小数位 | | 0表示正,1表示负 | -1022~+1023| 任意 |
由于计算机在计算的过程中,会把十进制数字先转换成二进制,然后做运算,因为浮点类型的小数位的限制而截断了运算完的二进制,这时候再把它转换成了十进制就产生了精度的问题。
举个例子: 0.1 + 0.2 = ?
- 0.1 转换成二进制:0.000110011001100110....
- 0.2 转换成二进制:0.001100110011001100....
- 0.000110011001100110.... + 0.001100110011001100.... = 0.0100110011001100110011001100110011001100110011001100...还很多,如果超过了 52 位就截断了。
- 然后转换成十进制:0.30000000000000004
解决方案
1.通过简单的四舍五入可以解决一些问题。
1 (0.1 + 0.2).toFixed(1);//0.3
但它也存在一些问题:
1 1.35.toFixed(1) // 1.4 正确 2 1.335.toFixed(2) // 1.33 错误 3 1.3335.toFixed(3) // 1.333 错误 4 1.33335.toFixed(4) // 1.3334 正确 5 1.333335.toFixed(5) // 1.33333 错误 6 1.3333335.toFixed(6) // 1.333333 错误
另外,它的返回结果类型是 String
。不能直接拿来做运算,因为计算机会认为是 字符串拼接
。
2.换算成整型计算
要彻底消除运算过程中的精度,需要将所有数字升位转化为整型了再做计算,计算完毕后再将最终结果进行相应的降位处理。
1 //除法 2 function accDiv(arg1,arg2){ 3 var t1=0,t2=0,r1,r2; 4 try{t1=arg1.toString().split(".")[1].length}catch(e){} 5 try{t2=arg2.toString().split(".")[1].length}catch(e){} 6 with(Math){ 7 r1=Number(arg1.toString().replace(".","")) 8 r2=Number(arg2.toString().replace(".","")) 9 return accMul((r1/r2),pow(10,t2-t1)); 10 } 11 } 12 //乘法 13 function accMul(arg1,arg2) 14 { 15 var m=0,s1=arg1.toString(),s2=arg2.toString(); 16 try{m+=s1.split(".")[1].length}catch(e){} 17 try{m+=s2.split(".")[1].length}catch(e){} 18 return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m) 19 } 20 //加法 21 function accAdd(arg1,arg2){ 22 var r1,r2,m; 23 try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0} 24 try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0} 25 m=Math.pow(10,Math.max(r1,r2)) 26 return (arg1*m+arg2*m)/m 27 } 28 //减法 29 function Subtr(arg1,arg2){ 30 var r1,r2,m,n; 31 try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0} 32 try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0} 33 m=Math.pow(10,Math.max(r1,r2)); 34 n=(r1>=r2)?r1:r2; 35 return ((arg1*m-arg2*m)/m).toFixed(n); 36 }