トピック 70 / 91· ソフトウェア工学とデジタル提供

セント単位で正確なコスト配分

合計金額(共有の助成金、インフラの請求書、予算影響額など)を素朴な割合計算で複数の受取先に分割すると、各部分を合計しても元の総額に戻らないことが日常的に起こります。セント単位で正確な配分はその解決策で、最小の通貨単位(セント)で動く整数/10進の手法であり、どれほど割り切れなくても、各部分がちょうど全体に合計されることを保証します。分割した合計を1セント単位で突き合わせなければならないソフトウェアエンジニア(給与計算、助成金の支払い、共有サービスの費用配賦)は、浮動小数点の割合ではなく、このパターンを必要とします。

なぜ重要か

これはエンタープライズソフトウェア工学における、名前の付いた基本的なパターンです。Martin FowlerのPatterns of Enterprise Application Architecture(2002年)がMoneyとAllocateを文書化しているのは、まさに「$100を3つに分ける」が、素朴なコードが頻繁に、しかも黙って間違える問題だからです。誤りは、誰かが帳簿を突き合わせて、各部分の合計が総額より1セント足りない(または多い)ことに気づいて初めて表面化します。ヘルスエコノミクスやNHSの財務業務では、これは学術的な話ではありません。予算影響の合計は拠点、年度、部局に分割され、共有のインフラやライセンス費用は人数や活動シェアで部門に按分されます。これらの分割はすべて正確に一致しなければなりません。合計と一致しない部分を渡された財務ディレクターは、モデル全体を信用しなくなるからです。

数式

素朴な(誤った)方法:
  部分_i = 丸め(合計 × 割合_i / Σ 割合)     — 各部分を個別に丸める

正確な方法(最大剰余法 / 「largest remainder allocation」):
  1. 基本_i = 切り捨て(合計_最小単位 × 割合_i / Σ 割合)   — 整数の最小単位(セント)のみ
  2. 剰余 = 合計_最小単位 − Σ 基本_i                       — 余ったセント、常に受取先の数未満
  3. ステップ1の小数部が最大の`剰余`個の受取先に、剰余が尽きるまで
     最小単位を1ずつ追加配分する

結果: Σ 部分_i == 合計、常に、構造上。

正確な方法は、部分を単独で丸めることは決してなく、配分全体を1つの操作として丸めます。これが、合計の不変条件を成り立たせます。

計算例

$100.00を3等分します(割合 = [1, 1, 1])。

素朴な方法: $100.00 ÷ 3 = $33.333…を個別に最も近いセントへ丸めると、各受取先に$33.33になります。合計すると$33.33 × 3 = $99.99で、1セントが消えており、目視で気づけるほど「間違っている」個別項目は一つもありません。

正確な方法: 基本は3つすべてで$33.33(切り捨て(10,000 / 3) = 3,333セントずつで合計9,999最小単位)、残りの剰余は1セント(10,000 − 9,999)です。その余った1セントは、除算で小数部が最大の受取先に渡ります。具体的にどの受取先かは同順位の決着に関する内部の詳細であり、呼び出し側が依存すべきものではありません。2つの受取先が$33.33、1つが$33.34となり、3つの部分はちょうど$100.00に合計されます。

これは、予算影響額の合計を拠点、コホート、会計年度に分割して公表された合計に突き合わせる必要がある場合に、予算影響分析が必要とするまさにその演算です。そうした多数の項目をずれなしで合計する、対になる問題については通貨安全なコスト集計を参照してください。

ソフトウェア工学との関連

これは文字どおりエンタープライズソフトウェアアーキテクチャの「Moneyパターン」であり、この種のバグに対する名前の付いた基本的なパターンで、一回限りの小細工ではありません。実際の財務照合の失敗は、まさにこの種のバグから製品に出荷されてきました。f64で計算した割合の分割を受取先ごとに丸め、元の合計と照らし合わせて確認しなかったものです。このリポジトリの総所有コストモジュールと直接つながっており、これは現在、年とオプションにわたる通常の浮動小数点のコストを合計しています。TCOや予算影響の合計を単に足し上げるのではなく按分しなければならない場合にも、同じ正確さの規律が当てはまります。

落とし穴

  • 最大剰余法ではなく「割合を出してから丸める」こと: 浮動小数点の割合で配分して各受取先を個別に丸めると、丸め誤差が累積し、特に受取先が多いと、合計に戻ることはほとんどありません。
  • 通貨の最小単位の指数を無視すること: すべての通貨が小数2桁だと思い込むこと。日本円は0桁で、3桁の通貨もあります。手作りの割合分割は通常2桁を決め打ちにして、他の通貨では黙って壊れます。正確な配分ルーチンは指数を通貨自体から読み取ります(ISO 4217)。
  • 配分済みの剰余を再配分すること: 冪等性のチェックなしに、以前の配分で残ったものに対して配分ルーチンを再び実行すると、同じ1セントを同じ受取先に二重に計上することがあります。

出典

  • Fowler M. "Patterns of Enterprise Application Architecture." Addison-Wesley, 2002 — MoneyとAllocateパターン。
  • ISO 4217 — 通貨および資金コードの標準規格で、各通貨の最小単位の指数を定義している。