部落格
Boring Expenses 如何儲存金額(以及為什麼用整數)
有一種經典的丟掉一分錢的方式。問幾乎任何一種程式語言 0.1 + 0.2 等於多少,它都會告訴你 0.30000000000000004。這不是語言的 bug——二進位制浮點數本來就是這樣工作的。它們無法精確表示大多數十進位制小數,於是細小的誤差就悄悄混了進來。算物理沒問題,算錢可不行。
對一個理財 App 來說,「差不多」就是錯誤答案。所以 Boring Expenses 從不把金額存成小數。
用整數儲存最小單位
處理錢的軟體一直以來的做法,是用最小單位計算,並把它儲存為整數。不存 $4.20,而是存 420——也就是分的數量。整數是精確的。整天加加減減、彙總求和,也不會積累任何舍入的塵埃。
Boring Expenses 更進一步,把金額存成非常精細的子單位(大致是「每一個單位一億份」),這樣既為各種貨幣和未來的需要留足了餘地,又讓一切保持為普通的 Int。與螢幕上顯示的數字之間的轉換隻發生在最邊緣——你輸入金額的時候,以及我們把它格式化顯示出來的時候。
// 僅為示意,並非實際程式碼:
let display = Decimal(amountInSubunits) / Decimal(subunitsPerUnit)
let text = display.formatted(.currency(code: userCurrency))
這對你意味著什麼
- 總額是精確的。 你的月度合計就是真實的合計,而不是隨著清單變長而偏差一分錢的數字。
- 同步保持一致。 一個整數在 iPhone、Watch 和 iPad 上含義完全相同,沒有任何需要調和的歧義。
- 匯出值得信賴。 你匯出的 CSV 與 App 裡的數字分毫不差。
這是一個小小的、不起眼的決定——正是我們喜歡的那種。你永遠不會看到它,而這正是重點:數字就是對得上。