Java中的浮点数类型float和double不能够进行精确运算。这个问题有时候非常严重。比如,经过double型直接计算,1.4×1.5有时会得出2.0999999999999996的结果,但实际上,应该得到2.10。而且,类似的情况并不仅限于乘法计算。
在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。所以,在商业计算中我们要用:java.math.BigDecimal 。
BigDecimal一共有4个构造方法,其中不属于int的有两个,它们是:
1、BigDecimal(double val)
Translates a double into a BigDecimal.
2、BigDecimal(String val)
Translates the String repre sentation of a BigDecimal into a BigDecimal.
上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。但是,第一个构造方法的详细说明中有:
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.
所以,如果需要精确计算,非要用String来构造BigDecimal不可。
一、解决方案:
现在已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来构造。
但是想像一下吧,如果要做一个加法运算,需要先将两个浮点数转为String,然后构造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。没错,这样太繁琐了。
下面的工具类Arith可以简化这部分操作。它提供以下静态方法,包括加减乘除和四舍五入:
public static double add(double v1,double v2)
public static double sub(double v1,double v2)
public static double mul(double v1,double v2)
public static double div(double v1,double v2)
public static double div(double v1,double v2,int scale)
public static double round(double v,int scale)
◆源文件Arith.java:
import java.math.BigDecimal;
public class
Arith{
//默认除法运算精度
private static final int DEF_DIV_SCALE = 10;
//这个类不能实例化
private Arith(){
}
/**
* 提供精确的加法运算。
* @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();
}
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();
}
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();
}
public
static double div(double v1,double v2){
return div(v1,v2,DEF_DIV_SCALE);
}
/**
*
提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
* 定精度,以后的数字四舍五入。
*/
public
static double div(double v1,double v2,int scale){
if(scale<0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return
b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
public
static double round(double v,int scale){
if(scale<0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return
b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
}