第 71 個主題,共 91 個· 軟體工程與數位化交付

幣種安全的成本彙總 (Currency-Safe Cost Rollup)

用常見的二進位制浮點數(f64)把大量貨幣條目——月度賬單、各地點成本、多年預算影響數位——加起來,會累積微小的表示誤差,因為大多數十進位制小數(如 $1,234.56)無法在二進位制浮點數中精確表示。每個誤差都很小,但一個把數百或數千個條目跨越多年相加的大模型,可能偏離幾分之一分——而且偏差取決於加法的順序,因而不可復現。用精確十進位制算術(或整數最小單位)做的貨幣彙總可以精確相加,與會計系統和複式記賬必須對到分的方式一致。

為什麼重要

這是一種有大量文獻記錄的基礎軟體缺陷:Goldberg 1991 年發表於 ACM Computing Surveys 的論文 "What Every Computer Scientist Should Know About Floating-Point Arithmetic",是說明二進位制浮點數為何無法精確表示大多數十進位制金額、以及為何把很多這樣的數相加會放大誤差的標準參考。NHS 的衛生經濟學和財務模型經常把多年、多個成本類別相加——總擁有成本和預算影響分析都會跨年份把許多 f64 成本條目加總。當模型必須對到分——手工算總數的審計必須得到完全相同的數位——算術本身就必須是精確十進位制,而不是浮點數。

計算方法

樸素彙總:        總計 = Σ f64(條目_i)       —— 取決於順序的漂移
幣種安全彙總:    總計 = Σ Decimal(條目_i)   —— 精確、可復現

應用百分比調整(例如應急費用):
  調整後 = 總計 × 乘數            —— 精確的 Decimal 結果,小數位數可能多於
                                    該幣種最小單位的指數
  舍入後 = 舍入(調整後, 幣種指數, 舍入規則)
                                  —— 舍入規則(half-up 對 half-even/銀行家舍入)
                                    必須明確指出

請注意兩步紀律:把精確的 Decimal 乘以乘數,可能得到比幣種實際使用更多的小數位(例如兩位小數的金額乘以兩位小數的乘數得到三位小數)——這一中間精度不會被自動截斷;只有帶有指明舍入規則的明確舍入步驟,才會把它降到幣種真正的最小單位指數。

算例

十二張完全相同的月賬單,每張 $1,234.56,用精確十進位制算術相加:$1,234.56 × 12 = 恰好 $14,814.72;相比之下,用 IEEE-754 雙精度把 f64 常量 1234.56 加十二次,可能因加法順序而偏離幾分之一分——這是真實且有文獻記載的缺陷型別,對建立在精確十進位制 Money 算術上的模型不是問題。

現在對該 $14,814.72 總額應用標準的 5% 預算影回應急費用(乘數 1.05):$14,814.72 × 1.05 = $15,555.456——三位小數,因為乘法是精確的,不會自動舍入到幣種的兩位小數。用銀行家舍入(half-even)明確舍入到 2 位小數,得到恰好 $15,555.46。

與軟體工程的關聯

這是“金融軟體用 Decimal,不用 float”這一原則背後的直接基礎課——與本倉庫的總擁有成本和預算影響分析模組明確相關,這兩個模組目前都用普通浮點數彙總成本。正確性的論證並不要求立即遷移這些模型;它指出的是系統何時必須對到分,因而不得用二進位制浮點數做其貨幣算術。關於與之配對的問題——如何拆分(而不是相加)一個總額而不損失一分錢——見精確到分的成本分攤。

陷阱

  • 鏈條中途轉成 float:在計算中途把貨幣值取出為浮點數(有些 Money 庫甚至把這種轉換方法命名為“lossy”作為明確警告),會悄悄放棄此後每一步計算的精確性保證。
  • “Decimal 太慢,不值得在意”:把精確十進位制算術當作不必要的負擔而否定,而財務報告需要的是正確性和可審計性,不是原始吞吐量。
  • 應用應急百分比卻不說明舍入規則:half-up 與 half-even(銀行家舍入)可能改變最後一分錢;舍入約定本身必須是明確宣告、可審計的選擇——關於 HM Treasury Green Book 對應急費用調整和樂觀偏差的指導,見成本效益分析,這正是該舍入步驟所適用的那類數位。

參考來源