面試經典題:計算 0.01 + 0.02 + ... + 1.0,直接用浮點數循環累加會產生誤差(結果 ≈ 49.5 或 50.4999)。本文分析浮點數誤差原因,給出三種精確計算方案。
// 錯誤 1:double 循環 double sum = 0; for (double d = 0.01; d <= 1.0; d += 0.01) sum += d; // 結果:49.50000000000003 // 錯誤 2:float 循環 float sum = 0; for (float d = 0.01f; d <= 1.0f; d += 0.01f) sum += d; // 結果:50.499976608902216
d <= 1.0 受誤差影響可能多跑/少跑一次。將問題轉化為整數運算:
// 0.01~1.0 對應 1~100 int sum = 0; for (int i = 1; i <= 100; ++i) sum += i; // 總和 = 5050 double result = sum / 100.0; // 50.5
或直接用等差數列求和公式:n*(a1+an)/2 = 100*101/2 = 5050。
用整數表示「分」:
int sumCent = 0; for (int i = 1; i <= 100; ++i) sumCent += i; // 5050 分 double result = sumCent / 100.0; // 50.5
BigDecimal sum = BigDecimal.ZERO;
BigDecimal step = new BigDecimal("0.01");
for (int i = 0; i < 100; ++i) {
sum = sum.add(step);
}
// 結果:50.50
// 或直接:
BigDecimal result = new BigDecimal("0.01").multiply(new BigDecimal("5050")); // 50.50
每次累加後四捨五入到分:
double sum = 0, d = 0.01;
for (int i = 0; i < 100; ++i) {
sum += d;
sum = (int)(sum * 100) / 100.0; // 截斷到分
d += 0.01;
}
// 結果:50.5
| 方法 | 精確度 | 性能 | 適用場景 |
|---|---|---|---|
| 整數累加/公式 | 絕對精確 | O(1)/O(n) | 固定步長等差數列,首選 |
| BigDecimal | 精確 | 較慢 | 金融/貨幣計算,任意精度需求 |
| 定點數 | 精確 | 快 | 固定小數位業務(如價格分) |
| 修正浮點 | 較精確 | 快 | 必須用浮點循環的場景 |
fabs(a-b) < 1e-9。S = n*(a1+an)/2,O(1) 解決本題。本題核心教訓:浮點數不適合精確累加、不適合作循環控制變量。正確答案 50.5 可通過 100*101/2/100 = 50.5 一行公式得到。面試中先給出公式法,再討論浮點數誤差原理與工程替代方案(整數分、BigDecimal),展現從理論到工程的綜合能力。