
금융 시스템이나 이커머스 솔루션을 개발할 때 가장 민감하게 다뤄야 하는 데이터는 무엇일까요? 바로 '돈'과 관련된 수치입니다. 자바에서 숫자를 다룰 때 흔히 사용하는 float나 double은 매우 빠르고 편리하지만, 치명적인 약점을 가지고 있습니다. 바로 부동 소수점(Floating Point) 오차입니다. 오늘은 왜 정밀한 계산에서 BigDecimal이 선택이 아닌 필수인지, 그 원리와 사용법을 심층 분석해 보겠습니다.
1. 왜 double로는 돈 계산을 하면 안 될까?
컴퓨터는 모든 데이터를 이진수(0과 1)로 처리합니다. 0.1이라는 십진수를 이진수로 변환하면 무한 소수가 발생하는데, 컴퓨터 메모리는 한정되어 있어 이를 중간에 잘라버립니다. 이것이 우리가 겪는 부동 소수점 오차의 원인입니다.
- 부동 소수점의 한계: IEEE 754 표준을 따르는
double은 근사치를 저장하기 때문에 연산이 반복될수록 오차가 누적됩니다. - BigDecimal의 원리:
BigDecimal은 숫자를 내부적으로 정수 배열(BigInteger)과 소수점 위치(scale)로 나누어 관리합니다. 즉, 십진수 그대로를 메모리에 올리기 때문에 오차가 발생하지 않습니다.
2. double vs BigDecimal 정밀도 비교
두 방식의 차이를 한눈에 파악할 수 있도록 실제 연산 결과 값을 비교해 보았습니다.
| 비교 항목 | primitive double | java.math.BigDecimal |
|---|---|---|
| 연산 결과 (0.1 + 0.2) | 0.30000000000000004 | 0.3 |
| 표현 방식 | 이진수 부동 소수점 (근사치) | 십진수 고정 소수점 (정확한 값) |
| 연산 속도 | 매우 빠름 (하드웨어 가속) | 상대적으로 느림 (객체 연산) |
| 주요 사용처 | 그래픽스, 통계, 일반 공학 계산 | 금융, 회계, 정밀 계측, 전자상거래 |
3. BigDecimal 사용 시 반드시 지켜야 할 'Golden Rule'
BigDecimal을 사용하더라도 잘못된 생성자를 쓰면 오차가 그대로 발생할 수 있습니다.
1) 반드시 문자열(String) 생성자를 사용하라
new BigDecimal(0.1)은 이미 오차가 발생한 double 값을 그대로 전달하는 꼴입니다. new BigDecimal("0.1") 또는 BigDecimal.valueOf(0.1)를 사용해야 정확합니다.
2) 불변 객체(Immutable)임을 명심하라
BigDecimal은 값이 변하지 않습니다. a.add(b)를 호출하면 a의 값이 변하는 것이 아니라 연산 결과가 담긴 새로운 객체를 반환합니다. 결과값을 반드시 변수에 다시 할당해야 합니다.
4. Sample Example: 실무형 연산 코드
할인율 적용과 반올림 처리가 포함된 비즈니스 로직 예제입니다.
import java.math.BigDecimal;
import java.math.RoundingMode;
public class PaymentCalculator {
public static void main(String[] args) {
// 1. 가격 설정 (반드시 문자열로 생성)
BigDecimal price = new BigDecimal("10000");
BigDecimal discountRate = new BigDecimal("0.125"); // 12.5% 할인
// 2. 할인 금액 계산
BigDecimal discountAmount = price.multiply(discountRate);
// 3. 최종 결제 금액 (소수점 둘째자리에서 반올림)
BigDecimal finalPrice = price.subtract(discountAmount)
.setScale(2, RoundingMode.HALF_UP);
System.out.println("할인 금액: " + discountAmount); // 1250.000
System.out.println("최종 결제 금액: " + finalPrice); // 8750.00
}
}
5. 결론: 언제 어떤 것을 써야 할까?
성능보다 정확성이 우선인 모든 도메인(재무, 회계, 법규 준수 등)에서는 무조건 BigDecimal을 사용해야 합니다. 반면, 데이터 양이 수억 건에 달하고 소수점 아래 미세한 오차가 결과에 큰 영향을 주지 않는 과학 기술 계산이나 대규모 통계 분석에서는 double이 효율적일 수 있습니다. 중요한 것은 "컴퓨터는 십진수를 완벽하게 이해하지 못한다"는 사실을 인지하고 개발 환경에 맞는 적절한 데이터 타입을 선택하는 안목입니다.
정보 출처
- Oracle Java SE 17 API Documentation: Class BigDecimal
- The Java Tutorials: Numbers and Strings
- Effective Java 3rd Edition (Joshua Bloch 저) - Item 60: Avoid float and double if exact answers are required
'Language > Java' 카테고리의 다른 글
| [JAVA] JVM의 내부 구조 완벽 해부 : 메모리 관리의 핵심 원리 (0) | 2026.01.25 |
|---|---|
| [JAVA] 개발 환경 구성 시 환경 변수(JAVA_HOME, PATH)를 설정하는 이유는? (0) | 2026.01.25 |
| [JAVA] Java assert 키워드 용도 : 디버깅 생산성을 높이는 방어적 프로그래밍 (0) | 2026.01.25 |
| [JAVA] 가변 인자(Varargs)란 무엇인가요? 유연한 메소드 설계의 핵심 (0) | 2026.01.25 |
| [JAVA] Enum Class의 특징과 장점 : 타입 안전성과 생산성을 높이는 핵심 전략 (0) | 2026.01.24 |