解决Java中浮点型数据计算时产生的精度误差

关于double和float的精度问题,在进行数值运算时,如果有float或者double类型的浮点数参与计算时,偶尔会出现结果不准确的情况。
比如:
/**
 * 解决Float与Double类型进度不准确的问题
 * @author XiaoYun 2013-08-17
 */
public class PrecisionOfFloatAndDouble {
public static void main(String[] args) {
testUnPrecision();
}
 
/**
 * 不准确问题示例
 */
private static void testUnPrecision() {
System.out.println("--------Java自身的Double类型有精度损失----------");
System.out.println(0.05+0.01); //0.060000000000000005
System.out.println(1.0-0.54);  //0.45999999999999996
System.out.println(4.015*1000); //4014.9999999999995
System.out.println(12.3/100); //0.12300000000000001 
}
}
 
执行程序后,控制台打印出来的数据为:
--------Java自身的Double类型有精度损失----------
0.060000000000000005
0.45999999999999996
4014.9999999999995
0.12300000000000001
 
下面开始说解决方法:
    首先,解决思路为使用java.math.BigDecimal类。查看JDK提供的api,可以使用 BigDecimal( String val) 构造方法(因为使用该方法构造的结果是完全可以预知的),因为使用 BigDecimal(double val) 构建出来的结果具有一定的不可预见性,而,其中val=Double(或者Float).toString(double1);,在使用该类中对应的加减乘除等方法来计算,该类中还提供了许多的计算方法,比如同时计算出商和余数的方法 divideAndRemainder( BigDecimal divisor) ,求商但是向下取(即如果商是3.5,那么就返回3)的方法
divideToIntegralValue( BigDecimal divisor) 等,这些方法我也没有全部研究,只是研究了这么多,如果你有兴趣的话可以自己研究下。
 
计算工具类:    
package com.xiaoyun.test;
 
import java.math.BigDecimal;
 
/**
 * 消除加减乘除的精度
 * @author XiaoYun 2013-08-17
 */
public class ArithUtil {
/**
 * 加法运算
 * @param v1
 * @param v2
 * @return
 */
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
 
/**
 * 减法运算
 * @param v1
 * @param v2
 * @return
 */
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
 
/**
 * 乘法运算
 * @param v1
 * @param v2
 * @return
 */
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
 
/**
 * 除法运算
 * @param v1 被除数
 * @param v2 除数
 * @return 商
 */
public static double div(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2).doubleValue();
}
 
/**
 * 除法运算
 * @param v1 被除数
 * @param v2 除数
 * @return 商和余数
 */
public static BigDecimal[] divideAndRemainder(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
BigDecimal[] arr = b1.divideAndRemainder(b2);
return arr;
}
 
/**
 * 求商(向下舍入)
 * @param v1
 * @param v2
 * @return
 */
public static BigDecimal divideToIntegralValue(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
System.out.println("------------");
return b1.divideToIntegralValue(b2);
}
}
 
测试类:
package com.xiaoyun.test;
 
import java.math.BigDecimal;
 
 
/**
 * 解决Float与Double类型进度不准确的问题
 * @author XiaoYun 2013-08-17
 */
public class PrecisionOfFloatAndDouble {
public static void main(String[] args) {
testUnPrecision();
System.out.println("----------使用BigDecimal消除精度影响------------\n" + ArithUtil.add(0.05, 0.01));
System.out.println(ArithUtil.sub(1.0, 0.54));
System.out.println(ArithUtil.mul(4.015, 1000));
System.out.println(ArithUtil.div(12.3, 10));
//得到商和余数
BigDecimal[] arr = ArithUtil.divideAndRemainder(12.3, 10);
System.out.println("得到商和余数");
for (BigDecimal bigDecimal : arr) {
System.out.println(bigDecimal);
}
System.out.println(ArithUtil.divideToIntegralValue(4.5, 2));
}
 
/**
 * 不准确问题示例
 */
private static void testUnPrecision() {
System.out.println("--------Java自身的Double类型有精度损失----------");
System.out.println(0.05+0.01);
System.out.println(1.0-0.54);
System.out.println(4.015*1000);
System.out.println(12.3/100);
}
}
 
到此,浮点数京都的问题就解决了。但是究其原因,是浮点数在计算机中是无法进行准确表示的,比如0.1在计算机中只是一个近似值,因此对浮点数运算结果具有不可预见性。如果两个浮点操作数的误差就能相互抵消,计算结果就能正确。比如0.3+0.4。
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值