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

精確到分的成本分攤 (Exact-Cents Cost Allocation)

用樸素的百分比算術把一筆總額——共享補貼、基礎設施賬單、預算影響數位——分給多個接收方,得到的各份加起來往往不等於原來的總額。精確到分的分攤是解決辦法:一種整數/十進位制方法,在幣種的最小單位(分)中運算,並保證各份加起來恰好等於總額,無論除法多麼不整齊。任何需要讓拆分後的總額對到分的軟體工程師——工資發放、補貼支付、共享服務分攤——都需要這個模式,而不是浮點百分比。

為什麼重要

這是企業軟體工程中一個有名字的基礎模式:Martin Fowler 的《Patterns of Enterprise Application Architecture》(2002)記錄了 Money 和 Allocate,正是因為“把 $100 分成三份”是樸素程式碼總會做錯的問題,而且是悄悄做錯——錯誤在有人對賬、發現各份比總額少(或多)一分錢時才浮現。在衛生經濟學和 NHS 財務工作中,這並非紙上談兵:預算影響數位要按地點、年份或信託機構拆分;共享的基礎設施和許可成本按員工人數或活動比例分攤到各部門。這些拆分每一個都必須恰好對上,因為一位財務總監若收到加起來不等於總額的各份,就會對整個模型失去信任。

計算方法

樸素方法(錯誤):
  份額_i = 舍入(總額 × 比例_i / Σ 比例)     —— 每份單獨舍入

精確方法(最大餘數法 / "largest remainder allocation"):
  1. 基數_i = 向下取整(以最小單位計的總額 × 比例_i / Σ 比例)   —— 僅整的最小單位(分)
  2. 餘數 = 以最小單位計的總額 − Σ 基數_i                      —— 剩餘的分,始終 < 接收方人數
  3. 給第 1 步中小數部分最大的 `餘數` 個接收方每人多加 1 個最小單位,
     直到餘數耗盡

結果:Σ 份額_i == 總額 始終成立,由構造保證

精確方法從不單獨舍入某一份——它把整個分攤作為一次運算來舍入,這正是求和不變數得以成立的原因。

算例

把 $100.00 分成三等份(比例 = [1, 1, 1])。

樸素方法:$100.00 ÷ 3 = $33.333…,每份單獨舍入到最接近的分,得到每個接收方 $33.33。合計:$33.33 × 3 = $99.99——丟了一分錢,而且沒有哪一行“錯”得足以被肉眼發現。

精確方法:三者的 基數 = $33.33(由 向下取整(10,000 / 3) = 3,333 分/接收方,共 9,999 個最小單位)。剩下 1 分(10,000 − 9,999)。這唯一剩下的一分錢歸除法中小數部分最大的接收方——具體是哪一個,是平局處理的內部細節,呼叫方不應依賴。兩個接收方得到 $33.33,一個得到 $33.34,三份加起來恰好 $100.00。

每當要把一個預算影響總額按地點、人群或財年拆分,並與已公佈的總額對賬時,預算影響分析需要的就是這種算術——關於與之配對的問題,即把許多這樣的條目相加而不漂移,見幣種安全的成本彙總。

與軟體工程的關聯

這恰恰就是企業軟體架構中的“Money 模式”——針對這類特定缺陷的有名字的基礎模式,而不是一次性的小技巧。真實的財務對賬故障曾因為同樣的缺陷進入生產:用 f64 計算的按比例拆分,按接收方舍入,從未與原始總額核對。它與本倉庫的總擁有成本模組直接相關,後者目前用普通浮點數跨年份和方案彙總成本——當 TCO 或預算影響總額需要被分攤而不只是相加時,同樣的精確性紀律同樣適用。

陷阱

  • 先算百分比再舍入,而不是用最大餘數法:用浮點百分比分攤,再對每個接收方單獨舍入,會疊加舍入誤差,很少能加回總額,接收方一多尤其如此。
  • 忽略幣種最小單位的指數:假定所有幣種都是 2 位小數——日元是 0 位,有些幣種是 3 位;手寫的按比例拆分常把 2 寫死,遇到其他幣種就悄悄出錯。精確分攤例程從幣種本身讀取指數(ISO 4217)。
  • 對已分攤的餘數重複分攤:對上一次分攤剩下的部分再次運作分攤例程,又沒有冪等性檢查,可能把同一分錢兩次記給同一個接收方。

參考來源

  • Fowler M. "Patterns of Enterprise Application Architecture." Addison-Wesley, 2002——Money 與 Allocate 模式。
  • ISO 4217——貨幣與資金程式碼標準,定義了每種幣種最小單位的指數。