1. 为什么需要 BigDecimal?
在 Java 中,float 和 double 类型由于采用 IEEE 754 浮点数标准,存在精度丢失问题,不适合用于金融、货币等需要高精度计算的场景。例如:
System.out.println(0.1 + 0.2); // 输出:0.30000000000000004
BigDecimal 是 Java 提供的用于高精度计算的类,能够避免浮点数运算的精度问题,适用于:
- 金融计算(金额、利率)
- 科学计算(高精度实验数据)
- 商业计算(税率、折扣)
2. BigDecimal 的核心特性
(1) 不可变性(Immutable)
BigDecimal 是不可变类,所有算术运算(如 add、subtract、multiply、divide)都会返回一个新的 BigDecimal 对象,而不会修改原对象:
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("2.0");
BigDecimal sum = a.add(b); // 返回新对象,a 和 b 不变
System.out.println(sum); // 12.5
(2) 精确的数值表示
BigDecimal 内部使用 BigInteger 存储未缩放的值(unscaled value)和一个缩放因子(scale),例如:
123.45存储为unscaledValue = 12345,scale = 21.2345存储为unscaledValue = 12345,scale = 4
这种存储方式避免了浮点数的二进制近似问题。
(3) 多种构造方式
BigDecimal 提供了多种构造方法:
BigDecimal b1 = new BigDecimal("123.456"); // 推荐:字符串构造,避免精度问题
BigDecimal b2 = BigDecimal.valueOf(123.456); // 推荐:静态工厂方法
BigDecimal b3 = new BigDecimal(123.456); // 不推荐:可能丢失精度
为什么推荐 String 构造?
new BigDecimal(0.1)实际上存储的是0.1000000000000000055511151231257827021181583404541015625(浮点数的二进制近似)new BigDecimal("0.1")精确存储0.1
3. BigDecimal 的运算
(1) 基本运算
| 方法 | 说明 |
|---|---|
add(BigDecimal) | 加法 |
subtract(BigDecimal) | 减法 |
multiply(BigDecimal) | 乘法 |
divide(BigDecimal) | 除法(需指定舍入模式) |
remainder(BigDecimal) | 取余 |
pow(int) | 幂运算 |
示例:
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3");
BigDecimal sum = a.add(b); // 13.5
BigDecimal product = a.multiply(b); // 31.5
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP); // 3.50(保留2位小数)
(2) 除法必须指定舍入模式
BigDecimal.divide() 在不能整除时必须指定 RoundingMode,否则会抛出 ArithmeticException:
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
// 错误:未指定舍入模式,抛出 ArithmeticException
// BigDecimal result = a.divide(b);
// 正确:指定舍入模式
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); // 3.33
(3) 比较运算
BigDecimal 使用 compareTo() 进行比较,不要用 equals():
BigDecimal a = new BigDecimal("10.0");
BigDecimal b = new BigDecimal("10.00");
System.out.println(a.equals(b)); // false(scale 不同)
System.out.println(a.compareTo(b)); // 0(数值相等)
4. 舍入模式(RoundingMode)
BigDecimal 支持多种舍入模式:
| 模式 | 说明 |
|---|---|
UP | 远离 0 舍入(1.1 → 2,-1.1 → -2) |
DOWN | 向 0 舍入(1.9 → 1,-1.9 → -1) |
CEILING | 向正无穷舍入(1.1 → 2,-1.1 → -1) |
FLOOR | 向负无穷舍入(1.9 → 1,-1.9 → -2) |
HALF_UP | 四舍五入(1.5 → 2,-1.5 → -2) |
HALF_DOWN | 五舍六入(1.5 → 1,-1.5 → -1) |
HALF_EVEN | 银行家舍入(1.5 → 2,2.5 → 2) |
示例:
BigDecimal num = new BigDecimal("1.235");
// 保留 2 位小数,四舍五入
BigDecimal rounded = num.setScale(2, RoundingMode.HALF_UP); // 1.24
5. BigDecimal 的优化与注意事项
(1) 使用静态工厂方法 valueOf()
BigDecimal.valueOf(double) 比 new BigDecimal(double) 更安全:
BigDecimal bad = new BigDecimal(0.1); // 不精确
BigDecimal good = BigDecimal.valueOf(0.1); // 精确
(2) 避免频繁创建对象
由于 BigDecimal 是不可变的,频繁运算会产生大量临时对象。在循环或高性能场景下,可以考虑优化:
BigDecimal sum = BigDecimal.ZERO; // 使用常量优化
for (int i = 0; i < 1000; i++) {
sum = sum.add(BigDecimal.valueOf(i));
}
(3) 注意 scale 的影响
BigDecimal 的 scale 会影响运算结果:
BigDecimal a = new BigDecimal("1.00");
BigDecimal b = new BigDecimal("1.0");
BigDecimal c = new BigDecimal("1");
System.out.println(a.equals(b)); // false(scale 不同)
System.out.println(a.compareTo(b)); // 0(数值相同)
6. 总结
BigDecimal适用于高精度计算,避免float/double的精度问题。- 推荐使用
String或valueOf()构造,避免精度丢失。 - 运算时必须处理舍入模式,特别是除法。
- 比较使用
compareTo(),而非equals()。 - 注意不可变性,运算结果需要重新赋值。
BigDecimal 是 Java 中处理精确计算的强大工具,正确使用可以避免许多数值计算的陷阱。
5219

被折叠的 条评论
为什么被折叠?



