java中float/double浮点数的计算失精度问题

ava中浮点数的计算
今天在数值计算时碰到一个问题.程序如下:
  double a = (3.3-2.4)/0.1;
  System.out.println(a);
你可能认为结果很简单,不就是9嘛,是事实上,结果为:8.999999998,为什么呢?我翻阅了一些资料,终于找出了原因.
为什么浮点数会丢失精度? 
十进制数的二进制表示可能不够精确
浮点数或是双精度浮点数无法精确表示的情况并不少见。浮点数值没办法用十进制来精确表示的原因要归咎于CPU表示浮点数的方法。这样的话您就可能会牺牲一些精度,有些浮点数运算也会引入误差。以上面提到的情况为例,2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。原因在于浮点数由两部分组成:指数和尾数。浮点数的值实际上是由一个特定的数学公式计算得到的。您所遇到的精度损失会在任何操作系统和编程环境中遇到。
注意: 您可以使用Binary Coded Decimal (BCD)库来保持精度。BCD数字编码方法会把每一个十进制数字位单独编码。 
类型失配
您可能混合了浮点数和双精度浮点数类型。请确定您在进行数学运算的时候所有的数据类型全部相同。
注意:float类型的变量只有7位的精度,而double类型的变量有15位的精度。
如何进行浮点数精度计算?            
      Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。
这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。
在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。
 
四舍五入
我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位):
public double round(double value){
    return Math.round(value*100)/100.0;
}
非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的
4.015*100=401.49999999999994
因此如果我们要做到精确的四舍五入,不能利用简单类型做任何运算
java.text.DecimalFormat也不能解决这个问题 ......

 

前段时间在做k的项目时,有一些客户端的计算,用js写的。发现了js精度的一些问题,借鉴某位大牛的一句话:“内事不决问百度,外事不决问google”的经典名言,上网淘了下相关资料,总结了下,以供参考:

1、js精度问题产生的根源:

这两个网址上洋洋洒洒说了一大堆,归根结底就是类型的精度问题导致,js的number类型按照ECMA的JavaScript标准,它的Number类型就是IEEE 754的双精度数值,相当于java的double类型。

详细的内容可以参考:

http://forum.eaw.com.cn/thread/10/1

http://blog.csdn.net/xiaocon/archive/2005/09/03/470709.aspx

 

2、js精度问题的解决方案:

 

1)由于js对于整数计算是没有误差的,可以先转化为整数进行计算

MathUtil.js

//浮点数相加

function dcmAdd(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 (dcmMul(arg1,m)+dcmMul(arg2,m))/m;

}

//浮点数相减

function dcmSub(arg1,arg2){

     return dcmAdd(arg1,-arg2);

}

//浮点数相乘

function dcmMul(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);

}

//浮点数除

function dcmDiv(arg1,arg2){

    return dcmMul(arg1,1/arg2);

}

 

Number.prototype.add = function (arg){

                   return dcmAdd(this, arg);

}

Number.prototype.sub = function (arg){

                   return dcmSub(this, arg);

}

 

Number.prototype.mul = function (arg){

                   return dcmMul(this, arg);

}

Number.prototype.div = function (arg){

                   return dcmDiv(this, arg);

}

 

 

2)模拟服务端bigdecimal的实现写一个js客户端的bigdecimal算法

    自己写一套客户端的bigdecimal算法或者参考现成的bigdecimal实现

    参考网址:http://stz-ida.de/html/oss/js_bigdecimal.html.en

 

3)利用ajax提交到后台进行计算

 

4)总结:

     1、尽量不要用JS做复杂的运算,特别是浮点数的运算。

     2、如果一定要进行浮点数的运算的话,先将浮点数转化为整形,再运算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值