Тема 71 з 91· Інженерія програмного забезпечення та цифрова доставка
Валютно безпечне агрегування витрат
Підсумовування багатьох грошових позицій — місячних рахунків, витрат за майданчиками, багаторічних цифр впливу на бюджет — звичайними двійковими числами з плаваючою комою (f64) накопичує дрібні похибки подання, бо більшість десяткових дробів (наприклад, 1 234,56 $) не можна точно подати у двійковій плаваючій комі. Кожна окрема похибка мізерна, але велика модель, що підсумовує сотні чи тисячі позицій за кілька років, може дрейфувати на частки цента — а дрейф залежить від порядку додавань, що робить його невідтворюваним. Валютне агрегування, виконане в точній десятковій (або цілочисельній у найменшій одиниці) арифметиці, підсумовує точно, відповідно до того, як бухгалтерські системи та подвійний запис мають сходитися до копійки.
Чому це важливо
Це добре задокументований, фундаментальний клас програмних помилок: стаття Голдберга 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 $, точно. Порівняйте з дванадцятикратним підсумовуванням літералу f64 1234.56 у подвійній точності IEEE-754, що може дрейфувати на частки цента залежно від порядку підсумовування — реальний, задокументований клас помилок, а не проблема для моделі, побудованої на точній десятковій арифметиці Money.
Тепер застосуйте стандартний резервний буфер впливу на бюджет 5% (множник 1,05) до цієї суми 14 814,72 $: 14 814,72 $ × 1,05 = 15 555,456 $ — три десяткові знаки, бо множення точне й автоматично не округлюється до двох десяткових знаків валюти. Явно округлене до 2 знаків банківським округленням (half-even) дає рівно 15 555,46 $.
Зв'язок з інженерією програмного забезпечення
Це пряма, фундаментальна наука за принципом «фінансове програмне забезпечення використовує Decimal, а не float» — вона явно пов'язана з модулями загальна вартість володіння та аналіз впливу на бюджет цього репозиторію, що обидва нині підсумовують звичайні витрати з плаваючою комою; аргумент коректності не вимагає негайно мігрувати ці моделі, але чітко вказує, коли система мусить сходитися до копійки і тому не повинна використовувати двійкову плаваючу кому для своєї грошової арифметики. Див. також точний розподіл витрат до цента — супутню проблему розподілу (а не підсумовування) сум без втрати центів.
Підводні камені
- Перетворення на
floatпосеред ланцюжка: витягнення грошового значення в число з плаваючою комою посеред обчислення (деякі бібліотекиMoneyнавіть називають цей метод перетворення на кшталт «lossy» як явне застереження) мовчки відкидає гарантію точності для кожного обчислення після цього моменту. - «Decimal надто повільний, щоб заморочуватися»: відкидання точної десяткової арифметики як зайвих накладних витрат, коли для фінансової звітності важать коректність і можливість аудиту, а не сира пропускна здатність.
- Застосування відсотка резерву без зазначення правила округлення: half-up проти half-even (банківське округлення) може змінити останній цент; сама конвенція округлення має бути зазначеним, придатним до аудиту вибором — див. аналіз витрат і вигод щодо настанов Green Book HM Treasury про резерви й коригування на optimism bias, саме ту цифру, до якої застосовують цей крок округлення.
Джерела
- Fowler M. «Patterns of Enterprise Application Architecture.» Addison-Wesley, 2002 — шаблон
Money. - Goldberg D. «What Every Computer Scientist Should Know About Floating-Point Arithmetic.» ACM Computing Surveys, 1991.
- HM Treasury, The Green Book — настанови щодо optimism bias та резервів для моделювання впливу на бюджет. https://www.gov.uk/government/publications/the-green-book-appraisal-and-evaluation-in-central-government/the-green-book-2020