下面我将从 基本用法、最佳实践、完整示例 和 常见陷阱 四个方面详细讲解 BigDecimal 的相加。

基本用法
使用 BigDecimal 进行加法,主要分为三步:
- 创建
BigDecimal对象:通常建议使用String构造函数,因为它能最精确地表示一个十进制数。 - 调用
add()方法:这是执行加法运算的核心方法,它会返回一个新的BigDecimal对象作为结果。 - 处理结果:将返回的结果用于后续计算或输出。
核心方法:
BigDecimal add(BigDecimal augend)
示例代码:
import java.math.BigDecimal;
public class BigDecimalAddExample {
public static void main(String[] args) {
// 1. 使用 String 构造函数创建 BigDecimal 对象(推荐)
BigDecimal num1 = new BigDecimal("0.1");
BigDecimal num2 = new BigDecimal("0.2");
// 2. 调用 add() 方法进行相加
// 注意:add() 方法不会修改 num1 的值,而是返回一个新的 BigDecimal 对象
BigDecimal result = num1.add(num2);
// 3. 输出结果
System.out.println("num1: " + num1); // 输出: num1: 0.1
System.out.println("num2: " + num2); // 输出: num2: 0.2
System.out.println("结果: " + result); // 输出: 结果: 0.3
}
}
最佳实践(非常重要)
错误地使用 BigDecimal 会导致与 double 同样的精度问题,以下是必须遵守的最佳实践:

a) 始终使用 String 构造函数
错误示范:
// 错误!double 类型本身就有精度问题,传入构造函数后问题依然存在 BigDecimal num1 = new BigDecimal(0.1); BigDecimal num2 = new BigDecimal(0.2); System.out.println(num1.add(num2)); // 输出可能是 0.3000000000000000166533453693773481063544750213623046875
1 这个 double 值在计算机中无法精确表示,它实际上是一个接近 0.1 的二进制浮点数,用这个不精确的值去构造 BigDecimal,就失去了 BigDecimal 的意义。
正确示范:
// 正确!使用 String 可以精确表示十进制数
BigDecimal num1 = new BigDecimal("0.1");
BigDecimal num2 = new BigDecimal("0.2");
System.out.println(num1.add(num2)); // 输出: 0.3
b) 避免使用 doubleValue()
在计算过程中,如果将 BigDecimal 的结果通过 doubleValue() 转换回 double,那么精度会再次丢失,通常只在需要与不要求精度的 API 交互时才使用此方法。

c) 处理舍入模式
在进行除法、开方等运算时,或者需要限制小数位数时,必须指定舍入模式,虽然加法运算本身不会引入舍入问题,但在金融计算中,所有运算结果通常都需要进行舍入。
BigDecimal 提供了多种舍入模式,如:
RoundingMode.HALF_UP:四舍五入(最常用)。RoundingMode.DOWN:直接舍弃多余小数(截断)。RoundingMode.CEILING:向正无穷方向舍入。RoundingMode.FLOOR:向负无穷方向舍入。
示例:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class RoundingExample {
public static void main(String[] args) {
BigDecimal price = new BigDecimal("99.995");
// 使用四舍五入模式,保留两位小数
BigDecimal roundedPrice = price.setScale(2, RoundingMode.HALF_UP);
System.out.println("原始价格: " + price); // 输出: 原始价格: 99.995
System.out.println("舍入后价格: " + roundedPrice); // 输出: 舍入后价格: 100.00
}
}
完整示例:模拟购物车计算
这个例子将综合运用 BigDecimal 的加法、乘法和舍入。
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
class Item {
private String name;
private BigDecimal price;
private int quantity;
public Item(String name, BigDecimal price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
public BigDecimal getSubtotal() {
// 使用 multiply 进行乘法运算,并设置结果的小数位数和舍入模式
return this.price.multiply(new BigDecimal(this.quantity)).setScale(2, RoundingMode.HALF_UP);
}
@Override
public String toString() {
return String.format("%s x%d = %s", name, quantity, getSubtotal());
}
}
public class ShoppingCart {
public static void main(String[] args) {
// 1. 创建商品列表,使用 String 构造价格
List<Item> items = new ArrayList<>();
items.add(new Item("苹果", new BigDecimal("5.99"), 3));
items.add(new Item("牛奶", new BigDecimal("12.50"), 2));
items.add(new Item("面包", new BigDecimal("8.75"), 1));
// 2. 计算 total,使用 BigDecimal 作为累加器
BigDecimal total = BigDecimal.ZERO; // 初始化为 0
System.out.println("--- 购物车明细 ---");
for (Item item : items) {
System.out.println(item);
// 3. 使用 add() 方法累加小计
total = total.add(item.getSubtotal());
}
System.out.println("\n总计: " + total);
}
}
输出结果:
--- 购物车明细 ---
苹果 x3 = 17.97
牛奶 x2 = 25.00
面包 x1 = 8.75
总计: 51.72
这个例子清晰地展示了如何正确地使用 BigDecimal 来处理涉及加法和乘法的商业计算,避免了精度丢失。
常见陷阱总结
-
直接使用
double或float构造BigDecimal- 问题:
double和float是二进制浮点数,无法精确表示很多十进制小数(如 0.1, 0.2)。 - 后果:
BigDecimal的精度优势被完全抵消。 - 解决方案:始终使用
new BigDecimal("字符串")。
- 问题:
-
修改原始对象
- 问题:
add(),subtract()等所有算术方法都是不可变的,它们不会修改调用者的值,而是返回一个新的BigDecimal对象。 - 后果:如果你写成
num1.add(num2)然后期望num1的值改变,num1依然是原来的值,计算结果被忽略了。 - 解决方案:必须用一个新的变量来接收返回值:
BigDecimal result = num1.add(num2);。
- 问题:
-
忘记处理舍入
- 问题:当需要限制小数位数或进行除法时,如果不指定舍入模式,会抛出
ArithmeticException。 - 后果:程序中断。
- 解决方案:使用
setScale()方法明确指定舍入模式(如RoundingMode.HALF_UP)和保留的小数位数。
- 问题:当需要限制小数位数或进行除法时,如果不指定舍入模式,会抛出
| 操作 | 正确做法 | 错误做法 |
|---|---|---|
| 创建对象 | new BigDecimal("0.1") |
new BigDecimal(0.1) |
| 加法运算 | BigDecimal result = a.add(b); |
a.add(b); (不接收返回值) |
| 处理舍入 | .setScale(2, RoundingMode.HALF_UP) |
忽略舍入,导致 ArithmeticException |
遵循这些原则,你就可以在 Java 中安全、精确地使用 BigDecimal 进行所有数学运算。
