BigDecimal

核心概念

BigDecimal是Java中用于精确计算的类,主要解决浮点数运算的精度问题。在金融、电商等对精度要求高的场景中必须使用。


一、为什么需要BigDecimal?

1.1 浮点数精度问题

// 浮点数运算的精度丢失
System.out.println(0.1 + 0.2);  // 0.30000000000000004
System.out.println(1.0 - 0.9);  // 0.09999999999999998
System.out.println(0.1 * 0.1);  // 0.010000000000000002

// 比较问题
System.out.println(0.1 + 0.2 == 0.3);  // false

1.2 原因分析

  • 计算机使用二进制存储浮点数
  • 0.1在二进制中是无限循环小数
  • 存储时必须截断,导致精度丢失
0.1 (十进制) = 0.0001100110011... (二进制,无限循环)

1.3 BigDecimal的解决方案

// ✅ 解决方案:比较时使用误差范围
double epsilon = 1e-10;
System.out.println(Math.abs(d1 - 0.3) < epsilon);  // true ✅


// 更佳:使用BigDecimal
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal c = a.add(b);
System.out.println(c);  // 0.3(精确)

二、BigDecimal的创建

2.1 构造方法对比

// ❌ 错误:使用double构造(精度丢失)
BigDecimal bad = new BigDecimal(0.1);
System.out.println(bad);  // 0.1000000000000000055511151231257827021181583404541015625

// ✅ 正确:使用String构造
BigDecimal good = new BigDecimal("0.1");
System.out.println(good);  // 0.1

// ✅ 正确:使用valueOf(内部转String)
BigDecimal better = BigDecimal.valueOf(0.1);
System.out.println(better);  // 0.1

2.2 构造方法选择

方式 精度 推荐度
new BigDecimal(double) ❌ 有精度问题 不推荐
new BigDecimal(String) ✅ 精确 推荐
BigDecimal.valueOf(double) ✅ 精确 推荐
new BigDecimal(int/long) ✅ 精确 推荐

2.3 常用常量

BigDecimal.ZERO;  // 0
BigDecimal.ONE;   // 1
BigDecimal.TEN;   // 10

三、BigDecimal运算

3.1 四则运算

BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3");

// 加法
BigDecimal sum = a.add(b);  // 13.5

// 减法
BigDecimal diff = a.subtract(b);  // 7.5

// 乘法
BigDecimal product = a.multiply(b);  // 31.5

// 除法(必须指定精度和舍入模式)
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP);  // 3.50

3.2 除法的特殊处理

BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");

// ❌ 错误:不指定精度,除不尽会抛异常
a.divide(b);  // ArithmeticException: Non-terminating decimal expansion

// ✅ 正确:指定精度和舍入模式
a.divide(b, 2, RoundingMode.HALF_UP);  // 3.33
a.divide(b, 4, RoundingMode.HALF_UP);  // 3.3333

3.3 舍入模式(RoundingMode)

模式 说明 示例(保留1位)
HALF_UP 四舍五入 2.5→3, 2.4→2
HALF_DOWN 五舍六入 2.5→2, 2.6→3
HALF_EVEN 银行家舍入 2.5→2, 3.5→4
UP 远离零方向舍入 2.1→3, -2.1→-3
DOWN 向零方向舍入(截断) 2.9→2, -2.9→-2
CEILING 向正无穷舍入 2.1→3, -2.9→-2
FLOOR 向负无穷舍入 2.9→2, -2.1→-3
UNNECESSARY 不需要舍入,否则异常 2.0→2
BigDecimal value = new BigDecimal("2.5");

value.setScale(0, RoundingMode.HALF_UP);    // 3(四舍五入)
value.setScale(0, RoundingMode.HALF_DOWN);  // 2(五舍六入)
value.setScale(0, RoundingMode.HALF_EVEN);  // 2(银行家舍入)
value.setScale(0, RoundingMode.UP);         // 3(远离零)
value.setScale

⬅️ StringBuilder系列 🏠 00-Java ➡️ 04-Java时间新API