Effective Java Item 60:需要精確結果時避免使用 float 和 double
整理 Effective Java 書中 Item 60: Avoid float and double if exact answers are required 心得筆記
主旨
Java 的 float 和 double 是為了高效處理近似值而設計,特別適合科學與工程領域。
但若場景需要精確數值(例如金額計算),它們就會成為麻煩來源,甚至導致錯誤結果。
點出問題:以為的 1.03 - 0.42,其實是 0.6100000000000001
浮點數並不能精確表達像 0.1 這樣的十進位小數。這會導致得到看起來荒謬的答案:
就算選擇四捨五入顯示,也無法保證運算結果在邏輯上正確。
範例:有 $1,要買 10¢、20¢、30¢…… 的糖果
以下這段用 double 計算你能買幾個糖果,並顯示剩下的錢:
輸出結果:
3 items bought.
Change: $0.3999999999999999
這根本就錯!你應該能買 4 個才對,並剛好把錢花光。
解法一:使用 BigDecimal
輸出正確:
4 items bought.
Money left over: $0.00
注意:建構 BigDecimal 請用字串建構子,如 new BigDecimal("1.00"),避免使用 new BigDecimal(1.00),因為會帶入不精確的浮點值!
解法二:用 int 或 long 做「整數處理」
用整數代表最小單位(如:1 元 = 100 分),可以避開浮點誤差:
這種方法簡潔、快速、無誤差,只是需要自己處理單位轉換與格式化輸出。
比較三種做法
| 方法 | 精確度 | 執行速度 | 撰寫便利性 | 適用場景 |
|---|---|---|---|---|
| float/double | ❌ | ✅ | ✅ | 科學計算、不需完全精準的統計 |
| BigDecimal | ✅ | ❌ | ❌ | 金融、帳務、法規計算 |
| int/long | ✅ | ✅ | ✅ (需自己轉換單位) | 小額金錢運算、遊戲內金幣等 |
小結
除非只需要「大約值」,否則千萬別用 float 或 double 來處理金額或其他需要絕對正確性的資料。
實務建議:
- 金額精算 → 用 BigDecimal
- 搭配字串建構子避免誤差
- 支援進位規則(例如四捨五入)
- 單位明確、金額不大 → 用 int/long
- 1 元 = 100 分、1 天 = 86400 秒這種設計很常見
- 若金額會超過 18 位數,才考慮用 BigDecimal 取代 long
精確比速度更重要的場景,請遠離
float和double!
Read other posts