在Java中涉及到交易和金融货币的时候为什么不能用double、float来计算?使用BigDecimal解决

在Java中涉及到交易和金融货币的时候为什么不能用double、float来计算?在日常使用中double、float的浮点计算经常会出现一些意外的情况,因为二进制不能精确的表示大部分十进制的小数,今天就来讨论一下如何正确的在Java中进行小数计算。

先看案例

我们定义两个 float 变量,一个是1.0-0.9,我们预期是0.1;另一个是0.9-0.8,我们预期也是0.1,然后我们对这两个数进行打印和比较:

float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;
System.out.println("a: " + a);
System.out.println("b: " + b);
System.out.println("a == b: " + (a == b));
Float x = a;
Float y = b;
System.out.println("x.equals(y): " + x.equals(y));

运行结果是:

a: 0.100000024
b: 0.099999964
a == b: false
x.equals(y): false
java中double、float的浮点计算的精度损失

是不是超出了你的预期?在二进制的世界里小数不能很好的被表示,因为十进制小数在与二进制转换的时候会运行除法,而除法有的是除不尽的,这就造成了精度损失。

使用BigDecimal解决

java中提供了BigDecimal这个超大的类,用来存储浮点型,使用它可以处理二进制误差问题。我们使用BigDecimal改造上面的运算逻辑:

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b); // 1.0 - 0.9
BigDecimal y = b.subtract(c); // 0.9 -0.8
System.out.println("x == y: " + (x == y)); // BigDecimal 是对象所以不能直接使用 == 进行比较
System.out.println("x.equals(y): " + x.equals(y));

运行结果是:

x == y: false
x.equals(y): true

使用BigDecimal的注意点

由于精度损失问题,使用new BigDecimal()的时候,我们只能使用参数为String的构造函数,也可以使用 BigDecimal.valueOf() 的方式,因为BigDecimal.valueOf()里面调用了Double.toString()方法,根据double实际表达能力对尾数进行了截断,我们可以查看一下JDK里面的源代码:

public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

分享此页面

Comments