JavaScript 소수점 오차 줄이기: 금액 계산에서 반올림 규칙을 고정하는 방법
웹에서 금액이나 단가를 계산할 때 가장 자주 만나는 문제 중 하나는 소수점 오차입니다.
예를 들어 0.1 + 0.2가 기대값인 0.3이 아니라 0.30000000000000004로 나오는 현상입니다.
이 글은 “왜 이런 값이 나오는가”를 짚고, 실무에서 오류를 줄이기 위해 반올림 시점과 자릿수 규칙을 고정하는 방법을 정리합니다.
문제
JavaScript의 기본 숫자형(Number)은 IEEE 754 배정밀도 부동소수점입니다.
이 형식은 대부분의 십진 소수를 이진수로 정확히 표현하지 못합니다.
그래서 다음처럼 아주 단순한 연산도 미세한 오차를 포함할 수 있습니다.
0.1 + 0.2; // 0.30000000000000004
1.005 * 100; // 100.49999999999999
문제는 이 오차가 화면 표시, 합계, 정렬, 조건 분기(===)에서 누적되면
결과 해석이 달라질 수 있다는 점입니다.
설명
실무에서는 보통 아래 세 가지 원칙을 같이 씁니다.
- 저장/중간 계산은 가능한 한 정수 단위로 처리
- 예: 원화는
원, 달러는cent단위 정수로 변환해 계산
- 예: 원화는
- 반올림 규칙을 한 곳에서 정의
- 예: 소수 둘째 자리까지 반올림이면 모든 경로에서 동일 함수 사용
- 표시(format)와 계산을 분리
- 계산값은 수치, 화면은 포맷터(
Intl.NumberFormat)로 처리
- 계산값은 수치, 화면은 포맷터(
특히 “언제 반올림할지”가 중요합니다.
- 연산마다 반올림하면 누적 오차가 커질 수 있고,
- 마지막 단계에서만 반올림하면 비즈니스 규칙과 어긋날 수 있습니다.
따라서 도메인 규칙(세금, 수수료, 단가 정책)에 맞춰 중간 반올림/최종 반올림 시점을 문서화해 두는 편이 안전합니다.
예시
아래 예시는 “소수 둘째 자리 반올림”을 고정하는 함수입니다.
Number.EPSILON을 더해 경계값 오차를 줄입니다.
function roundTo(value, digits = 2) {
const factor = 10 ** digits;
return Math.round((value + Number.EPSILON) * factor) / factor;
}
const subtotal = 19.9 * 3; // 59.699999999999996
const tax = subtotal * 0.1; // 5.97 근처 값
const total = roundTo(subtotal + tax, 2);
console.log(total); // 65.67
정밀도가 더 중요한 케이스(정산, 회계, 거래 기록)라면,
- 금액을 최소 화폐 단위 정수로 저장하거나,
- decimal 연산 라이브러리(예: decimal.js) 사용을 검토하는 것이 일반적입니다.
내부 링크 후보
요약
- JavaScript
Number는 부동소수점 특성상 십진 소수 오차가 발생할 수 있습니다. - 금액 계산은 정수 단위 처리, 반올림 함수 통일, 표시/계산 분리로 안정성을 높일 수 있습니다.
- 핵심은 “완벽한 0오차”보다 팀이 합의한 반올림 규칙을 일관되게 적용하는 것입니다.