目录

Java数值问题汇总

double转成BigDecimal的精度损失问题

如果直接用构造方法将double数值转成BigDecimal,可能存在损失精度的问题:

1
2
3
4
5
6
final BigDecimal b1 = new BigDecimal(0.48);
final BigDecimal b2 = BigDecimal.valueOf(0.48);
// 0.479999999999999982236431605997495353221893310546875
System.out.println(b1);
// 0.48
System.out.println(b2);

可以看到,第一种方式丢失了精度。原因是0.48这个浮点数实际上并不能非常精确的表示0.48这个double值,因为二进制表示的小数部分长度是有限的。

为了避免丢失精度,需要调用官方提供的valueOf方法,实际上底层是将浮点数先转成字符串,再转成BigDecimal:

1
2
3
4
5
6
7
    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));
    }

因此,也可以直接传入字符串来构造BigDecimal:

1
2
3
final BigDecimal b3 = new BigDecimal("0.48");
// 0.48
System.out.println(b3);

判断BigDecimal是否相等

在判断两个BigDecimal是否相等时,不能使用equals(),原因是equals()除了比较数值是否相等外,还会比较标度scale。从业务来考虑,通常只需要对比数值就行了,此时应该用compareTo()方法:

1
2
3
4
5
6
7
8
public static void main(final String[] args) {
    BigDecimal a = new BigDecimal("0.1");
    BigDecimal b = new BigDecimal("0.10");
	// false
    System.out.println(a.equals(b));
	// 0
    System.out.println(a.compareTo(b));
}

compareTo()会返回-1,0和1,从上述代码可以看出来,a和b的标度不同,a的标度是0.1,b的标度是0.10,所以equals()返回的false,但是由于二者数值相同,所以compareTo()返回了0。