--- title: "03-BigDecimal" created: 2025-11-25 tags: - Java --- # BigDecimal ## 核心概念 BigDecimal是Java中用于**精确计算**的类,主要解决浮点数运算的精度问题。在金融、电商等对精度要求高的场景中必须使用。 --- ## 一、为什么需要BigDecimal? ### 1.1 浮点数精度问题 ```java // 浮点数运算的精度丢失 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的解决方案 ```java // ✅ 解决方案:比较时使用误差范围 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 构造方法对比 ```java // ❌ 错误:使用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 常用常量 ```java BigDecimal.ZERO; // 0 BigDecimal.ONE; // 1 BigDecimal.TEN; // 10 ``` --- ## 三、BigDecimal运算 ### 3.1 四则运算 ```java 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 除法的特殊处理 ```java 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 | ```java 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 ``` --- ⬅️ [[02-StringBuilder系列|StringBuilder系列]] 🏠 [[00-Java|00-Java]] ➡️ [[04-Java时间新API|04-Java时间新API]]