Javascript 中的浮点数精度丢失问题

我们前端在做计算过程中,特别时小数的计算,都会遇到意想不到的结果,通常我们在价格的计算中遇到很多问题,这是由于 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 }

 

上一篇:[SR]-查看Linux(Ubuntu Server)服务器IP地址


下一篇:Apache Avro 与 Thrift 比较