现在的位置: 首页 > 技术分享 > 正文
Javascript中的浮点数运算
2012年12月27日 技术分享本站原创 ⁄ 共 1639字 暂无评论 ⁄ 被围观 2,828+

前端开发工程师们在做使用javascript编程时,经常会遇到这样一个问题:浮点数运算出错。比如非常简单的一个计算,0.1 + 0.2,在浏览器中执行以下的测试代码:

alert(0.1 * 0.2);

弹出对话框中居然显示的是0.30000000000000004!真是匪夷所思啊,难道计算机连1加2等于3算不对吗,还是javascript本身存在bug呢?为了弄清楚这个问题,我们首先要从计算机是如何存储和处理数字开始探寻。

众所周知,计算机是采用二进制来处理数字的。但是二进制有一个问题,那就是不能精确表示浮点数,比如十进制的0.1,转化为二进制之后,就成了一个无限循环小数:

0.00011001100110011…(循环0011)

这下就麻烦了,因为计算机不可能给一个数据分配无限的存储空间,所以不论是采用单精度浮点数(32位)还是双精度浮点数(64位),都必须对这样的无限小数进行截位处理。这样,看似简单的0.1,在计算机中的竟变成了一个二进制的近似值,这就是误差的根源所在了。这些近似值参与计算之后,计算结果中自然也可能会存在误差。最终,当二进制的计算结果被转换为十进制数字呈现在我们面前时,如果计算结果中的误差足够大,那么在转换后就可能会有所体现了。所以,我们看到了前面的试验结果: 0.1+0.2=0.30000000000000004。

原来问题出在计算机对浮点数的处理上,貌似有点麻烦了,毕竟二进制是无法改变的。看来我们需要想一下对策了。

既然浮点数本身就存在误差,那么最有效的方式,当然就是不让计算机进行浮点数运算了。我们可以对浮点数进行”升位”(乘以10的n次幂),转换为计算机可以精确处理的整数,计算完之后,再”降位”(除以10的n次幂)即可。比如0.1+0.2,是不是就可以变为 (1+2)/10 来处理呢,好的,我们来试验一下,在浏览器中执行下面的代码:

alert((0.1*10 + 0.2*10)/10);

弹出0.3,果然有效!不过先不要太高兴,仔细看一下,上面的处理代码还是有问题的,因为在”升位”时,0.1*10已经进行了浮点数运算,最终得到正确结果,只能说是侥幸而已。可以再做个试验:

alert(512.06*100);

弹出51205.99999999999,这次就没那么幸运了。

那么我们该如何进行精确的”升位”操作呢?没辙,只能用”笨”办法了:把数字转换为字符串形式,找到小数点的位置,然后去数位数吧。具体实现方式有很多种,在这里提供一段代码供大家参考:

function myAdd(num1,num2){

       var s1 = num1.toString();

       var s2 = num2.toString();

       var r1 = 0; //第一个数字的升位数

       var r2 = 0;//第二个数字的升位数

       try{

              r1 = s1.split(".")[1].length;

       }catch(e){

       }

       try{

              r2 = s2.split(".")[1].length;

       }catch(e){

       }

       s1 = s1.replace(".", "");

       s2 = s2.replace(".", "");

       if(r1 > r2){

              for(var m = 0; m < r1 - r2; m++){

                     s2 += 0;

              }

       }else if(r2 > r1){

              for(var m = 0; m < r2 - r1; m++){

                     s1 += 0;

              }

       }

       var n = Math.pow(10, Math.max(r1, r2)); //降位数

      return (Number(s1) + Number(s2)) / n;

}

×