System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(1.03 - 0.42); // 0.6100000000000001
System.out.println(190.00 * 100); // 18999.999999999996
IEEE 754 浮點數無法精確表示十進制小數(如 0.1 二進制無限循環),導致財務系統不可接受的精度損失。金額、利率、匯率、庫存計量必須用 BigDecimal。
BigDecimal a = new BigDecimal("0.1"); // 精確 0.1
BigDecimal b = new BigDecimal(0.1); // 0.1000000000000000055511151231257827021181583404541015625
System.out.println(a); // 0.1
System.out.println(b); // 0.10000000000000000555...
鐵律:只用 String 構造,或用 BigDecimal.valueOf(double)(內部調用 Double.toString() 獲得標準表示)。
BigDecimal x = new BigDecimal("12.34");
BigDecimal y = new BigDecimal("5.67");
BigDecimal sum = x.add(y); // 18.01
BigDecimal diff = x.subtract(y); // 6.67
BigDecimal prod = x.multiply(y); // 69.9678
BigDecimal quot = x.divide(y, 4, RoundingMode.HALF_UP); // 2.1764
所有運算返回新對象,原對象不變,支持鏈式調用:a.add(b).multiply(c)。
// 除不盡拋 ArithmeticException: Non-terminating decimal expansion
BigDecimal d = BigDecimal.ONE.divide(BigDecimal.valueOf(3)); // 崩潰!
// 正確:指定保留小數位 + 舍入模式
BigDecimal d = BigDecimal.ONE.divide(BigDecimal.valueOf(3), 4, RoundingMode.HALF_UP); // 0.3333
| 模式 | 行為 | 示例 (保留 1 位) |
|---|---|---|
| HALF_UP | 四捨五入,5 向上 | 1.5→2, 1.4→1, -1.5→-2 |
| HALF_DOWN | 五捨六入,5 向下 | 1.5→1, 1.6→2, -1.5→-1 |
| HALF_EVEN | 銀行家算法,5 向偶數 | 1.5→2, 2.5→2, -1.5→-2 |
| UP | 遠離零方向 | 1.1→2, -1.1→-2 |
| DOWN | 趨向零方向(截斷) | 1.9→1, -1.9→-1 |
| CEILING | 向正無窮 | 1.1→2, -1.9→-1 |
| FLOOR | 向負無窮 | 1.9→1, -1.1→-2 |
| UNNECESSARY | 精確除盡否則異常 | 1/2 拋異常 |
金融標準:中國會計準則/大多數業務採用 HALF_UP;統計學/銀行結算常用 HALF_EVEN 減少累積偏差。
BigDecimal v = new BigDecimal("67.75");
System.out.println(v.divide(BigDecimal.valueOf(5), 1, RoundingMode.HALF_DOWN)); // 13.5
System.out.println(v.divide(BigDecimal.valueOf(5), 4, RoundingMode.HALF_DOWN)); // 13.5500
BigDecimal v2 = new BigDecimal("67.751");
System.out.println(v2.divide(BigDecimal.valueOf(5), 1, RoundingMode.HALF_DOWN)); // 13.6 <-- 陷阱!
System.out.println(v2.divide(BigDecimal.valueOf(5), 4, RoundingMode.HALF_DOWN)); // 13.5502
原因:HALF_DOWN 比較的是「捨去位之後的所有數字」與 0.5 的大小。
13.5502 捨去小數點後 1 位 → 看 0.0502 > 0.05 → 進位 → 13.6。
建議:金融業務統一用 HALF_UP,語義直觀,無此陷阱。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1.00");
a.equals(b); // false (scale 不同:1 vs 2)
a.compareTo(b); // 0 (數值相等)
a.stripTrailingZeros().equals(b.stripTrailingZeros()); // true
鐵律:比較數值大小只用 compareTo;equals 同時比較數值與標度。
BigDecimal n = new BigDecimal("123.4500");
n.stripTrailingZeros().toPlainString(); // "123.45" (去尾零,非科學計數法)
n.toPlainString(); // "123.4500" (保留標度)
n.toEngineeringString(); // "123.45E0" (工程記數法)
static final BigDecimal HUNDRED = BigDecimal.valueOf(100);MathContext:MathContext mc = new MathContext(10, RoundingMode.HALF_UP);long 分/厘存儲(整數運算),落地時除 100/1000 轉 BigDecimal。new BigDecimal(String) 或 BigDecimal.valueOf(double);scale + RoundingMode;RoundingMode.HALF_UP;compareTo,不用 equals;stripTrailingZeros().toPlainString();