주제 71 / 91· 소프트웨어 공학과 디지털 전달

통화 안전 비용 집계

월별 청구서, 사업장별 비용, 다년간 예산 영향 수치 같은 많은 금액 항목을 일반적인 이진 부동소수점(f64) 숫자로 합산하면 작은 표현 오차가 누적됩니다. 대부분의 십진 소수(예: $1,234.56)는 이진 부동소수점으로 정확히 표현할 수 없기 때문입니다. 각각의 오차는 아주 작지만, 수백에서 수천 개의 항목을 수년에 걸쳐 합산하는 큰 모델은 1센트의 몇 분의 일만큼 어긋날 수 있으며, 그 어긋남은 덧셈이 일어나는 순서에 따라 달라지므로 재현이 불가능합니다. 정확한 십진(또는 정수 최소 단위) 연산으로 수행한 통화 집계는 정확하게 합산되며, 회계 시스템과 복식부기가 센트 단위까지 맞아야 하는 방식과 일치합니다.

왜 중요한가

이는 문서화가 잘 되어 있는 근본적인 소프트웨어 버그의 한 부류입니다. 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)은
                                            명시적으로 밝혀야 함

2단계 규율에 주목하세요. 정확한 Decimal 금액에 승수를 곱하면 통화가 실제로 쓰는 것보다 많은 소수 자릿수가 나올 수 있습니다(예: 소수 둘째 자리 금액에 소수 둘째 자리 승수를 곱하면 소수 셋째 자리가 됩니다). 그 중간 정밀도는 자동으로 반올림되어 사라지지 않으며, 밝혀 둔 반올림 규칙을 가진 명시적 반올림 단계만이 그것을 통화의 실제 최소 단위 지수로 줄여 줍니다.

계산 예시

각각 $1,234.56인 월별 청구서 열두 건을 정확한 십진 연산으로 합산합니다. $1,234.56 × 12 = $14,814.72, 정확히 그렇습니다. 이를 f64 리터럴 1234.56을 IEEE-754 배정밀도로 열두 번 합산하는 경우와 비교해 보세요. 합산 순서에 따라 1센트의 몇 분의 일만큼 어긋날 수 있으며, 이는 실재하고 문서화된 버그 부류이지 정확한 십진 Money 연산으로 만든 모델에는 문제가 되지 않습니다.

이제 이 $14,814.72 합계에 표준적인 예산 영향 예비비 버퍼 5%(승수 1.05)를 적용합니다. $14,814.72 × 1.05 = $15,555.456이며, 곱셈이 정확하고 통화의 소수 둘째 자리로 자동 반올림되지 않기 때문에 소수 셋째 자리가 나옵니다. 이를 은행가 반올림(half-even)으로 소수 둘째 자리까지 명시적으로 반올림하면 정확히 $15,555.46이 됩니다.

소프트웨어 공학과의 연관성

이는 "금융 소프트웨어는 float가 아니라 Decimal을 쓴다"의 배경에 있는 직접적이고 근본적인 교훈입니다. 이 저장소의 총소유비용과 예산 영향 분석 모듈에 명시적으로 연결되며, 둘 다 현재는 일반 부동소수점 비용을 합산합니다. 정확성에 대한 논거는 그 모델들을 즉시 이전하라고 요구하지는 않지만, 시스템이 언제 센트 단위까지 맞아야 하는지, 그래서 금액 연산에 이진 부동소수점을 써서는 안 되는지를 정확히 밝힙니다. 합산이 아니라 분할을 할 때 센트를 잃지 않는 짝이 되는 문제는 센트 단위 정확한 비용 배분을 참고하세요.

함정

  • 연산 도중에 float로 변환하기: 계산 중간에 금액을 부동소수점 숫자로 꺼내는 것(일부 Money 라이브러리는 이 변환 메서드에 명시적 경고로 "lossy" 같은 이름을 붙이기도 합니다)은 그 지점 이후의 모든 계산에서 정확성 보장을 조용히 버립니다.
  • "Decimal은 너무 느려서 신경 쓸 가치가 없다": 재무 보고에서는 원시 처리량이 아니라 정확성과 감사 가능성이 중요한데도, 정확한 십진 연산을 불필요한 오버헤드로 치부하는 것.
  • 반올림 규칙을 밝히지 않고 예비비 비율을 적용하기: half-up과 half-even(은행가 반올림)은 마지막 1센트를 바꿀 수 있습니다. 반올림 관행 자체가 밝혀 둔 감사 가능한 선택이어야 합니다. 이 반올림 단계가 적용되는 바로 그런 수치인, 예비비와 낙관 편향 보정에 관한 HM Treasury 그린북 지침은 비용편익분석을 참고하세요.

출처