1,000 円三個人分,那 1 円去哪了:尾數與精度的小學問
我們團隊做 Spidee 時,花最多時間修的 bug 不是匯率、不是 LINE 整合,而是一個 0.01。
有位用戶回報:他轉了 16.67 元給朋友之後,餘額顯示 −33.33,而不是 0。他明明照著 app 說的金額轉,帳卻沒清掉。追下去發現,是電腦算 500 ÷ 3 的時候,內部存成 166.66666…,三個人的份加起來變成 499.99999…,最後差了那 0.01。
這篇想講的是:分帳裡的尾數不是小事。它決定了帳「清不清得掉」,也決定了使用者信不信任這個工具。
每種貨幣的「最小單位」不一樣
先講一個很多人沒注意的事:不同貨幣的最小單位不同。
- 日圓沒有小數。1,000 円分三人,只能是 333、333、334。
- 台幣在法規上有「角」和「分」,但實際生活裡沒人用。你不可能轉 333.33 元給朋友。
- 美元、歐元有兩位小數。10 美元分三人是 3.33、3.33、3.34。
- 有些貨幣(例如巴林第納爾)有三位小數,不過大部分人一輩子不會遇到。
所以「均分」這件事,正確的講法不是「除以人數」,而是**「除以人數,四捨五入到該貨幣的最小單位,然後把餘數分配掉」**。少了後半句,帳永遠清不乾淨。
餘數給誰:三種做法
假設三個人在京都錦市場買了 1,000 円的醬菜要均分。333 × 3 = 999,多出 1 円。這 1 円有三種處理方式:
給付款人。付錢的人多承擔 1 円,理由是他本來就墊了錢,多一塊少一塊對他影響最小。缺點是如果同一個人整趟都在付款,他會累積十幾個 1 円,雖然金額微不足道,但看起來「總是他吃虧」。
輪流給。第一筆多的給 A,第二筆給 B,第三筆給 C。公平,但難以解釋,而且刪掉一筆帳之後順序就亂了。
固定規則給第一個人。例如永遠給分攤名單裡排最前面的那個人。這是我們團隊最後選的做法:不是最公平,但最可預測。任何人隨時打開那筆帳,都可以自己算出「誰多付 1 円」,不需要知道歷史。
一年下來,這個人大概多付了幾十円。跟「帳永遠差 1 円清不掉」的挫折感相比,這是很便宜的代價。
電腦不會算 0.1 + 0.2
第二個坑是技術的。大部分程式語言算 0.1 + 0.2,得到的不是 0.3,是 0.30000000000000004。這不是 bug,是電腦用二進位存小數的先天限制。
在分帳裡,這件事的後果是:如果你用一般的小數運算做加總,幾十筆帳之後一定會出現 0.01 的誤差。就是開頭那位用戶遇到的問題。
解法有兩種。一種是全部用整數算:把 166.67 存成 16667「分」,加總完再除回來。另一種是每一步都四捨五入到貨幣的最小單位,不讓誤差有機會累積。我們兩種都用,因為多幣別換算時還會多一層匯率乘法,那裡的小數更長。
我們最後定的規則是:匯率換算保留十位小數,但任何一個會顯示給人看的金額,都必須四捨五入到該貨幣的最小單位。使用者看到的每一個數字,都是他可以真的轉出去的數字。
怎麼檢查你用的 app 有沒有處理好
你不需要看程式碼,做一個小測試就知道:
- 建一個三人群組,記一筆 1,000 元均分。
- 看每個人的分攤金額。如果顯示 333.33,而你的貨幣沒有小數,這是第一個警訊。
- 照 app 說的金額把帳結掉。
- 看餘額有沒有全部變成 0。
如果結完剩下 0.01、−0.01、或者 0.33,這個 app 沒有處理尾數。它不會影響你出國一趟,但它會讓你每次結算都覺得「怪怪的」,然後有一天你就不用它了。
分帳工具的可信度,是從這種 1 円的地方建立起來的。