BigDecimal 能保證小數精確計算,核心原理是**十進制縮放整數運算**,而非二進制浮點近似。本文從內部結構、運算邏輯、與 float/double 對比三個維度解析。
// 簡化版 BigDecimal 內部結構
public class BigDecimal {
private final BigInteger intVal; // 無標度值:整數
private final int scale; // 標度:小數點右移位數
// 實際值 = intVal / 10^scale
}
"123.456" → intVal = 123456, scale = 3123456 / 10^3 = 123.456
// 1.23 + 4.5
// intVal: 123 (scale=2), 45 (scale=1)
// 對齊標度:45 → 450 (scale=2)
// 整數相加:123 + 450 = 573
// 結果:intVal=573, scale=2 → 5.73
// 1.2 × 3.45
// intVal: 12 (scale=1), 345 (scale=2)
// 整數相乘:12 × 345 = 4140
// 標度相加:1 + 2 = 3
// 結果:intVal=4140, scale=3 → 4.140
// 1 ÷ 3 無限循環小數
BigDecimal result = BigDecimal.ONE.divide(BigDecimal.valueOf(3),
10, RoundingMode.HALF_UP); // 保留10位,四捨五入
// 0.3333333333
| 特性 | float/double (IEEE 754) | BigDecimal |
|---|---|---|
| 表示方式 | 二進制科學計數法:±1.xxx × 2^exp | 十進制縮放:intVal × 10^(-scale) |
| 0.1 表示 | 無限循環二進制:0.0001100110011... → 截斷誤差 | 精確:intVal=1, scale=1 |
| 運算誤差 | 累積放大 | 無誤差(整數運算) |
| 性能 | 硬件加速、極快 | 軟件實現、較慢 |
| 適用場景 | 科學計算、圖形、非金融 | 金融、貨幣、精確計價 |
BigInteger 存儲**十進制數值對應的整數**,而非二進制浮點近似值。例如十進制 123456 在內存中是二進制 11110001001000000,但這代表**確切的整數 123456**,不存在精度損失。BigDecimal 只是在其基礎上記錄「小數點位置」。
// 錯誤
new BigDecimal(0.1); // 實際值 0.10000000000000000555...
// 正確
new BigDecimal("0.1"); // 字符串構造,精確
BigDecimal.valueOf(0.1); // 靜態工廠,內部用 Double.toString
new BigDecimal("1.0").equals(new BigDecimal("1.00")); // false (scale 不同)
// 比較數值大小用
a.compareTo(b) == 0; // true
BigDecimal.ONE.divide(BigDecimal.valueOf(3)); // ArithmeticException
// 必須
divide(divisor, scale, RoundingMode.HALF_UP)
BigDecimal 精度來源:**十進制縮放整數運算**。將小數轉為「無標度整數 + 標度」,所有運算歸約為 BigInteger 整數運算,避開二進制浮點無法精確表示十進制小數的根本缺陷。代價是性能下降,但金融領域**精確性優先於性能**。