• 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 }
  • 相关阅读:
    Cannot find class [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter]
    HTTP的长连接和短连接
    Nginx(三)nginx 反向代理
    Nginx(四)nginx 负载均衡
    postgresql 数据库 INSERT 或 UPDATE 大量数据时速度慢的原因分析
    低层次父母,喜欢不停地“讲道理”,而高层次父母,会做2件事
    oracle数据库数据量如何计算,怎么查看oracle数据库数据量大小?
    Git的eclipse插件(下载、抓取、提交、恢复、比较)
    Linux 上 定时备份postgresql 数据库的方法
    Nginx(二)nginx.conf 配置文件
  • 原文地址:https://www.cnblogs.com/hzn1995/p/14695939.html
Copyright © 2020-2023  润新知