我们知道使用浮点数计算会丢失精度. 在计算金额时, 推荐使用 BigDecimal 类进行计算, 下面给出示例
我们先来验证浮点数丢失精度的问题
/**
* double 丢失精度
*/
@Test
public void testDoubleLossOfAccuary() {
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);
}
输出结果
可以看到精度确实丢失了. 很不准确.
我们使用 BigDecimal 来操作
加法
/**
* 加法
*/
@Test
public void testAdd() {
BigDecimal b1 = BigDecimal.valueOf(1.1);
BigDecimal b2 = BigDecimal.valueOf(1.11);
// BigDecimal 不可变类, 原对象不变, 结果是方法的返回值
BigDecimal result = b1.add(b2);
System.out.println(result);
}
减法
/**
* 减法
*/
@Test
public void testSubtract() {
BigDecimal b1 = BigDecimal.valueOf(1.1);
BigDecimal b2 = BigDecimal.valueOf(1.11);
BigDecimal result = b1.subtract(b2);
System.out.println(result);
}
乘法
/**
* 乘法
*/
@Test
public void testMultiply() {
BigDecimal b1 = BigDecimal.valueOf(1.1);
BigDecimal b2 = BigDecimal.valueOf(1.11);
BigDecimal result = b1.multiply(b2);
System.out.println(result);
}
除法
/**
* 除法
* 固定选择重载方法 divide(BigDecimal, int, int)
* 避免遇到除不尽 / 没指定进位方式时 而抛出异常. 例如 10 / 3
* java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
*/
@Test
public void testDivide() {
BigDecimal b1 = BigDecimal.TEN;
BigDecimal b2 = BigDecimal.valueOf(3);
// 保留 2 位小数位, 四舍五入
BigDecimal result = b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);
System.out.println(result);
}
可以看出结果都是完全正确的.
注意事项:
BigDecimal
构造时, 选择BigDecimal.valueOf(long / double)
/new BigDecimal(String)
不会有精度丢失问题, 如果选择new BigDecimal(long / double)
会有精度丢失问题BigDecimal
是不可变类, 我们想要拿到结果需要获取返回值才行, 原BigDecimal
对象值并不会改变BigDecimal
做除法时, 选择重载方法divide(BigDecimal, int, int)
指定保留位数和进位方式, 以确保除法除不尽时 (例如 10 / 3), 程序不会报错BigDecimal
输出为字符串时, 选择调用toPlainString()
不会出现科学计数法