浮點數陷阱人人知曉:
System.out.println(0.05 + 0.01); // 0.060000000000000005
原因:二進制無法精確表示十進制小數。金融、銀行、電商計價等場景必須使用 BigDecimal。
BigDecimal = unscaledValue × 10^(-scale)
| 方式 | 代碼 | 推薦度 | 說明 |
|---|---|---|---|
| double 構造器 | new BigDecimal(0.1) | ❌ 禁止 | 攜帶浮點誤差:0.10000000000000000555... |
| String 構造器 | new BigDecimal("0.1") | ✅ 推薦 | 精確表示 |
| valueOf 工廠 | BigDecimal.valueOf(0.1) | ✅ 推薦 | 內部用 Double.toString,效果同 String |
調用 setScale(scale, roundingMode) 時指定:
ROUND_UP | 遠離零方向 | 1.6 | 稅費計算(向上取整) |
| ROUND_DOWN | 趨向零方向(截斷) | 1.5 | 截斷多餘位 |
| ROUND_CEILING | 向正無窮 | 1.6 (正) / 1.5 (負) | 保證不小於真值 |
| ROUND_FLOOR | 向負無窮 | 1.5 (正) / 1.6 (負) | 保證不大於真值 |
| ROUND_HALF_UP | 四捨五入(≥0.5 進位) | 1.6 | **最常用**,貨幣結算 |
| ROUND_HALF_DOWN | 五捨六入(>0.5 進位) | 1.5 | 統計學特定需求 |
| ROUND_HALF_EVEN | 銀行家算法(五進雙不進) | 1.6 (前位奇) / 1.5 (前位偶) | 減少累積誤差,IEEE 754 默認 |
| ROUND_UNNECESSARY | 斷言無需捨入,否則拋異常 | 異常 | 精度校驗 |
// 金額計算範本
public class MoneyUtils {
private static final int SCALE = 2;
private static final RoundingMode MODE = RoundingMode.HALF_UP;
public static BigDecimal add(BigDecimal a, BigDecimal b) {
return a.add(b).setScale(SCALE, MODE);
}
public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
return a.multiply(b).setScale(SCALE, MODE);
}
public static BigDecimal divide(BigDecimal a, BigDecimal b) {
return a.divide(b, SCALE, MODE); // 除法必須指定精度與模式
}
}
stripTrailingZeros().toPlainString()compareTo() == 0 比較數值大小ArithmeticExceptionBigDecimal 是 Java 高精度計算的基石。牢記:String/valueOf 創建、HALF_UP 計價、divide 必指定精度,即可避開 99% 的坑。