关于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。