十進制小數 (如 0.1) **無法用有限二進制小數精確表示**,只能無限循環近似,導致儲存時發生捨入誤差。
0.1 (十進制) = 0.00011001100110011... (二進制循環)
float 佔 32 位 (23 位尾數) → 截斷/捨入
double 佔 64 位 (52 位尾數) → 更精確但仍非精確
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(1.0 - 0.9); // 0.09999999999999998
| 場景 | 方案 |
|---|---|
| 貨幣/金融計算 | BigDecimal (字串構造) + 設定 MathContext/scale + RoundingMode |
| 科學計算/圖形 | double + 容差比較 Math.abs(a-b) < 1e-9 |
| 顯示格式化 | String.format("%.2f", val) / DecimalFormat |
| 整數縮放 | 金額以「分」為單位存 long (10000 代表 100.00 元) |
// 錯誤:new BigDecimal(0.1) 會帶入 double 的誤差
// 正確:字串構造
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b); // 精確 0.3
// 除法必須指定精度與捨入模式
BigDecimal div = a.divide(b, 10, RoundingMode.HALF_UP);