본문 바로가기
language/java

BigDecimal을 사용하는 이유

by 죄니안죄니 2026. 6. 16.
반응형

이번 글은 실무에서 가장 많이 실수하는 클래스 중 하나이다.

특히 금융, 쇼핑몰, 결제 시스템에서는 거의 필수이며,
Java 면접에서도 자주 등장한다.

많은 사람들이

 
double price = 0.1 + 0.2;

System.out.println(price);
 

의 결과가

0.3
 

일 것이라고 생각한다.

하지만 실제 결과는

0.30000000000000004
 

이다.

왜 이런 일이 발생할까?


BigDecimal을 사용하는 이유

BigDecimal은 부동소수점 오차 없이 정확한 십진수 계산을 위한 클래스이다.


먼저 double부터 이해하자.

많은 사람들이

 
double value = 0.1;
 

0.1
 

이 그대로 저장된다고 생각한다.

하지만 실제로는 아니다.


컴퓨터는 2진수만 이해한다.

컴퓨터는

0

1
 

두 개의 숫자만 저장할 수 있다.


정수는 쉽게 표현된다.

10(decimal)

↓

1010(binary)
 

하지만

0.1(decimal)
 

은 어떨까?


0.1을 2진수로 바꾸면

0.1

↓

0.0001100110011001100110011...
 

처럼 끝없이 반복된다.

마치

1 / 3

=

0.333333333...
 

이 끝없이 반복되는 것과 같다.


그런데 메모리는 유한하다.

double은

64bit만 사용할 수 있다.

그래서

0.000110011001100110011001100110011...
 

0.0001100110011001100110011001100110
 

정도에서 잘라 저장한다.


즉,

실제로 저장되는 값은

0.1

≠

0.1000000000000000000

≈

0.10000000000000000555...
 

이다.


그래서 0.1 + 0.2

 
System.out.println(0.1 + 0.2);
 

실제로는

0.10000000000000000555...

+

0.20000000000000001110...

=

0.30000000000000004440...
 

이다.


출력

0.30000000000000004
 

실무에서 얼마나 위험할까?

쇼핑몰을 생각해 보자.

상품

0.1원

+

0.2원

=

0.3원
 

이어야 한다.


그런데

0.30000000000000004원
 

이 되면

세금 계산

↓

할인 계산

↓

총액 계산
 

을 거치면서 오차가 계속 누적된다.


금융 시스템에서는 절대 허용되지 않는다.


그래서 BigDecimal이 등장했다.

BigDecimal은

2진수

×
 

이 아니라

십진수 자체를

문자열 형태로 관리

+

정확한 계산
 

을 수행한다.


예제

 
BigDecimal a =
        new BigDecimal("0.1");

BigDecimal b =
        new BigDecimal("0.2");

System.out.println(a.add(b));
 

결과

0.3
 

정확하게 나온다.


내부적으로는 어떻게 저장될까?

double

0.1

↓

2진수 근사값
 

BigDecimal

"0.1"

↓

정확한 십진수 표현

↓

BigInteger + scale
 

로 저장한다.


BigDecimal 내부 구조

실제로는

대략 이런 형태이다.

 
BigDecimal

------------------

unscaledValue

scale

------------------
 

예를 들어

123.45
 

unscaledValue = 12345

scale = 2
 

로 저장된다.


즉,

12345

↓

소수점 두 자리

↓

123.45
 

이다.


가장 많이 하는 실수

다음 코드를 보자.

 
BigDecimal value =
        new BigDecimal(0.1);
 

많은 사람들이

0.1
 

이 저장될 것이라고 생각한다.


하지만

0.1(double)은 이미

0.10000000000000000555...
 

이다.


그래서

 
System.out.println(
        new BigDecimal(0.1)
);
 

결과는

0.100000000000000005551115...
 

처럼 출력된다.


올바른 방법

항상

 
new BigDecimal("0.1")
 

또는

 
BigDecimal.valueOf(0.1)
 

를 사용한다.


BigDecimal은 immutable이다.

String처럼

수정되지 않는다.


 
BigDecimal a =
        new BigDecimal("10");

a.add(
    new BigDecimal("5")
);

System.out.println(a);
 

결과

10
 

이다.


왜?


add()는

새 객체를 반환한다.

10

↓

add(5)

↓

15(new object)
 

이다.


그래서

 
a = a.add(
        new BigDecimal("5")
);
 

처럼 다시 받아야 한다.


equals() 함정

다음 코드를 보자.

 
BigDecimal a =
        new BigDecimal("1.0");

BigDecimal b =
        new BigDecimal("1.00");

System.out.println(a.equals(b));
 

결과는 무엇일까?


많은 사람들이

true
 

라고 생각한다.


실제 결과

false
 

이다.


왜냐하면

BigDecimal의 equals는

값

+

scale
 

까지 비교한다.


1.0

↓

value=10

scale=1
 

1.00

↓

value=100

scale=2
 

scale이 다르므로

equals는 false이다.


compareTo()

실무에서는

 
a.compareTo(b)
 

를 많이 사용한다.


 
a.compareTo(b) == 0
 

결과

true
 

이다.


compareTo는

순수 값만 비교
 

하기 때문이다.


연산도 메서드이다.

BigDecimal은

연산자를 사용할 수 없다.


더하기

 
a.add(b);
 

빼기

 
a.subtract(b);
 

곱하기

 
a.multiply(b);
 

나누기

 
a.divide(b);
 

divide()도 함정이 있다.

 
BigDecimal.ONE.divide(
        new BigDecimal("3")
);
 

결과

ArithmeticException
 

이다.


왜냐하면

1 / 3

=

0.333333333...
 

무한소수이기 때문이다.


그래서

실무에서는

 
BigDecimal.ONE.divide(

    new BigDecimal("3"),

    2,

    RoundingMode.HALF_UP

);
 

처럼 사용한다.


결과

0.33
 

이다.


실무에서는 언제 사용할까?


금액

 
price

tax

discount

totalPrice
 

환율

USD

KRW

JPY
 

정밀 계산

금융

회계

정산

포인트
 

언제 double을 사용할까?


센서 값

온도

습도

거리
 

그래픽

좌표

게임

물리 계산
 

AI

행렬 계산

벡터 계산
 

여기서는 속도가 중요하므로

double을 사용한다.


그림으로 정리

double

0.1

↓

2진수 변환

↓

근사값 저장

↓

오차 가능
 

BigDecimal

"0.1"

↓

정확한 십진수 저장

↓

정확한 계산

↓

금융 시스템 사용
 

면접 포인트

BigDecimal을 사용하는 이유는?

double은 IEEE 754 부동소수점 방식으로 저장되어 오차가 발생할 수 있기 때문에, 정확한 십진수 계산이 필요한 경우 BigDecimal을 사용한다.


왜 new BigDecimal(0.1)은 위험한가?

0.1(double) 자체가 이미 근사값이므로 그 오차까지 그대로 BigDecimal에 저장되기 때문이다.


실무에서 권장하는 생성 방법은?

 
new BigDecimal("0.1")
 

또는

 
BigDecimal.valueOf(0.1)
 

이다.


equals()와 compareTo() 차이는?

  • equals() : 값 + scale 비교 (1.0 ≠ 1.00)
  • compareTo() : 순수 값만 비교 (1.0 = 1.00)

다음 글 추천

다음은 ThreadLocal 원리를 추천한다.

이 글부터는 다시 Spring과 강하게 연결된다.

  • ThreadLocal이 무엇인지
  • 왜 static처럼 보이는데 사용자마다 값이 다른지
  • Spring Security의 사용자 정보
  • TransactionSynchronizationManager
  • MyBatis SqlSession
  • @Transactional

이 모든 것이 하나의 ThreadLocal 개념으로 연결되는 것을 이해하게 된다.

반응형

'language > java' 카테고리의 다른 글

ThreadLocal Memory Leak  (0) 2026.06.16
ThreadLocal 원리  (0) 2026.06.16
StringBuilder vs StringBuffer  (0) 2026.06.16
Integer Cache  (0) 2026.06.16
AutoBoxing / Unboxing  (0) 2026.06.16

댓글