现在的位置: 首页 > 综合 > 正文

js的转换精度问题以及解决方案

2014年11月10日 ⁄ 综合 ⁄ 共 2680字 ⁄ 字号 评论关闭

1. 让人迷惑的现象

让我们来做个简单的实验——把下面的代码转贴到地址栏中并按确定键:

javascript:alert(0.1 + 0.2)  

结果返回: 0.30000000000000004, 的确如此,你可能会感动迷惑,为什么不是0.3? 为什么js不能像其他语言(如c或java)一样printf出正确的结果呢? 这是不是js的bug呢? 其实这些疑问在当时也迷惑着我。其实我们随意搜索下google就能知道答案,这是由于十进制到二进制的转换导致的精度问题!因为计算机执行的是二进制算术,当一个十进制数不能准确的转化着二进制数时,这种精度误差就无法避免。如果对这简单的原因描述还是迷惑的话,那就接着看文章后面的具体描述吧!

 

2. 认识javascript的浮点运算

 学过js的同学都知道,js中的数字都是用浮点数表示的,并规定使用IEEE 754 标准的双精度浮点数表示。

 2.1 IEEE 754 标准的浮点数简介(呵呵,这可是大学里学习的组成原因知识噢)

 IEEE 754 规定了两种基本浮点格式:单精度和双精度。

 IEEE单精度格式具有24 位有效数字精度(包含符号号),并总共占用32 位。

 IEEE双精度格式具有53 位有效数字精度(包含符号号),并总共占用64 位。

说明: 基本浮点格式是固定格式,相对应的十进制有效数字分别为7位和17位。基本浮点格式对应的C/C++类型为float和double。

 

 2.2 实验过程推导

 有了以上的理论知识,我们可以模拟计算机对之前实验的运算做个简单的过程展示:

 十进制0.1  
 => 二进制0.00011001100110011…(循环0011)   
 =>尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-4(二进制移码为00000000010),符号位为0  
 => 计算机存储为:0 00000000100 10011001100110011…11001  
 => 因为尾数最多52位,所以实际存储的值为0.00011001100110011001100110011001100110011001100110011001  
 而十进制0.2  
 => 二进制0.0011001100110011…(循环0011)  
 =>尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-3(二进制移码为00000000011),符号位为0  
 => 存储为:0 00000000011 10011001100110011…11001  
 因为尾数最多52位,所以实际存储的值为0.00110011001100110011001100110011001100110011001100110011  
 那么两者相加得:      
 0.00011001100110011001100110011001100110011001100110011001  
+  0.00110011001100110011001100110011001100110011001100110011 (确认??)  
 =  0.01001100110011001100110011001100110011001100110011001100  
 转换成10进制之后得到:0.30000000000000004  

3.解决方法

<script>
//加法函数,用来得到精确的加法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
//调用:accAdd(arg1,arg2)
//返回值:arg1加上arg2的精确结果
function accAdd(arg1,arg2){
    var r1,r2,m;
    try{r1=arg1.toString().split(".")[1].length;}catch(e){r1=0;}
    try{r2=arg2.toString().split(".")[1].length;}catch(e){r2=0;}
    m=Math.pow(10,Math.max(r1,r2));
    return (arg1*m+arg2*m)/m;
}
//减法函数减数即为加数加上数的负数
function accSub(arg1,-arg2){
    var r1,r2,m;
    try{r1=arg1.toString().split(".")[1].length;}catch(e){r1=0;}
    try{r2=arg2.toString().split(".")[1].length;}catch(e){r2=0;}
    m=Math.pow(10,Math.max(r1,r2));
    return (arg1*m+arg2*m)/m;
}
//乘法函数,用来得到精确的乘法结果
//说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
//调用:accMul(arg1,arg2)
//返回值:arg1乘以arg2的精确结果
function accMul(arg1,arg2)
{
    var m=0,s1=arg1.toString(),s2=arg2.toString();
    try{m+=s1.split(".")[1].length;}catch(e){}
    try{m+=s2.split(".")[1].length;}catch(e){}
    return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m);
}

//除法函数,用来得到精确的除法结果
//说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
//调用:accDiv(arg1,arg2)
//返回值:arg1除以arg2的精确结果
function accDiv(arg1,arg2){
    var t1=0,t2=0,r1,r2;
    try{t1=arg1.toString().split(".")[1].length;}catch(e){}
    try{t2=arg2.toString().split(".")[1].length;}catch(e){}
    with(Math){
        r1=Number(arg1.toString().replace(".",""));
        r2=Number(arg2.toString().replace(".",""));
        return (r1/r2)*pow(10,t2-t1);
    }
}
</script>

抱歉!评论已关闭.