티스토리 뷰
자바스크립트로 이커머스 장바구니 합계 기능을 구현하거나, 금융 솔루션의 이자 계산 로직을 작성하던 개발자라면 누구나 한 번쯤 기이한 현상을 마주합니다. 분명히 0.1과 0.2를 더했는데, 화면에는 0.3이 아닌 0.30000000000000004라는 숫자가 출력되는 상황입니다. 이는 단순한 '버그'가 아니라, 현대 컴퓨팅 체계가 숫자를 다루는 근본적인 방식에서 비롯된 필연적인 결과입니다.
단순히 "자바스크립트가 숫자를 잘 못 다루네"라고 치부하기엔, 이 오차로 인해 결제 금액이 틀어지거나 정밀한 데이터 분석 결과가 왜곡되는 등 실무에서 발생하는 리스크가 매우 큽니다. 10년 차 에디터이자 수석 연구원의 시각으로, 이 현상의 기술적 실체와 2026년 기준 가장 효율적인 대응 전략을 분석합니다.
IEEE 754: 10진수 세계와 2진수 컴퓨터의 충돌
자바스크립트는 모든 숫자를 IEEE 754 표준에 따른 '64비트 부동소수점(Double Precision)' 형식으로 처리합니다. 우리가 사용하는 10진수 소수는 컴퓨터 내부에서 2진수로 변환되어 저장되는데, 여기서 첫 번째 비극이 시작됩니다.
1. 무한 소수의 늪
10진수 세계에서 1/3이 0.3333...으로 나누어떨어지지 않듯, 2진수 세계에서는 0.1이나 0.2 같은 평범한 숫자도 무한 소수가 됩니다. 0.1을 2진수로 변환하면 0.0001100110011...이 반복되며 끝없이 이어집니다.
2. 53비트의 한계 (Mantissa)
컴퓨터의 메모리는 유한합니다. IEEE 754 표준은 소수점 아래 숫자를 저장할 공간(가수부)을 딱 52비트(숨겨진 비트 포함 53비트)까지만 할당합니다. 무한히 이어지는 2진수 소수의 뒷부분을 강제로 잘라내거나 반올림하는 과정에서 '정밀도 손실(Precision Loss)'이 발생하며, 이것이 우리 눈에 오차로 나타나는 것입니다.
오차 발생의 메커니즘 비교 분석
단순 연산뿐만 아니라 큰 수와 작은 수를 더할 때 발생하는 '정보 누락' 현상도 주의해야 합니다. 아래 표는 자바스크립트에서 숫자를 다룰 때 발생하는 대표적인 정밀도 한계를 정리한 데이터입니다.
| 구분 | 값 / 한계치 | 발생 현상 및 영향 |
|---|---|---|
| Safe Integer 범위 | ±(2^53 - 1) | 이 범위를 벗어나면 1 단위의 정확한 정수 계산이 불가능해짐 |
| Number.EPSILON | 약 2.22e-16 | 두 숫자 사이의 수용 가능한 최소 오차 범위 (비교 연산 시 활용) |
| 0.1 + 0.2 오차값 | 4.440892...e-17 | 실제 합산 결과에서 0.3과의 미세한 차이 발생 |
실무 환경별 최적의 해결 솔루션 3단계
문제의 원인을 파악했다면, 이제 상황에 맞는 도구를 선택해야 합니다. 2026년 현재 가장 권장되는 방식은 프로젝트의 성격에 따라 나뉩니다.
- 정수 전환법 (The Integer Scaling Strategy)
가장 고전적이면서도 강력한 방법입니다. 소수점 연산을 피하기 위해 모든 숫자에 10의 거듭제곱을 곱해 정수로 만든 뒤 연산하고, 마지막에 다시 나누는 방식입니다. 예를 들어 달러($) 단위를 다룬다면 센트(Cent) 단위로 변환하여 계산합니다.
const result = (0.1 * 10 + 0.2 * 10) / 10; // 0.3 - BigInt와 전용 라이브러리 활용
소수점 정밀도가 생명인 금융 및 과학 계산 엔진에서는 자바스크립트 기본
Number타입을 지양해야 합니다. 현재 업계 표준으로 자리 잡은 Big.js나 Decimal.js 라이브러리를 사용하면 임의 정밀도 연산이 가능해 오차를 0%로 줄일 수 있습니다. - 반올림 오차 허용 (Number.EPSILON)
단순히 두 값의 일치 여부를 확인할 때는 직접 비교(==) 대신
Number.EPSILON을 활용한 오차 허용 비교 방식을 도입해야 합니다.const isEqual = Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true
수석 연구원의 제언: 잘못된 상식 바로잡기
- toFixed()는 만능이 아닙니다:
toFixed(2)는 결과값을 '문자열'로 반환하며, 내부적으로도 반올림 방식에 따라 예상치 못한 결과를 낼 수 있습니다. 화면 출력용으로만 사용하고 연산 중간 단계에서 사용하는 것은 지양하십시오. - 브라우저 엔진 차이는 미미합니다: 부동소수점 오차는 크롬(V8), 사파리(WebKit), 파이어폭스(Gecko) 등 엔진의 문제가 아니라 컴퓨터 하드웨어의 부동소수점 처리 장치(FPU) 레벨에서 발생하는 공통 사안입니다.
- BigInt는 정수 전용입니다: ES2020에서 도입된
BigInt는 소수점 이하를 다룰 수 없습니다. 소수점 계산이 필요하다면 여전히Number타입을 가공하거나 외부 라이브러리를 써야 합니다.
결론적으로, 자바스크립트에서 숫자를 다룰 때는 "모든 소수점 연산은 오차를 내포하고 있다"는 전제하에 방어적으로 코드를 작성해야 합니다. 단순 서비스라면 정수 전환법으로 충분하지만, 데이터의 무결성이 중요한 시스템이라면 반드시 Decimal.js와 같은 검증된 솔루션을 도입하는 것이 미래의 기술 부채를 방지하는 최선의 선택입니다.
추가적으로 특정 통화 계산(예: KRW vs USD)이나 대규모 통계 데이터 처리 시의 최적화 설정이 궁금하시다면, 관련 라이브러리의 precision 설정 가이드를 참고하시기 바랍니다.
이번 포스팅을 준비하며 저 역시 다시 한번 기본기를 다질 수 있었습니다. 끝까지 정독해 주셔서 진심으로 고맙습니다.

