티스토리 뷰

Java

[Java] BigDecimal

snail voyager 2025. 4. 19. 18:39
728x90
반응형

BigDecimal은 Java에서 정밀한 소수 계산이 필요할 때 사용하는 클래스

float나 double 타입은 부동소수점 오차가 있어서 정확한 소수 계산이 필요할 때는 적합하지 않다.

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("10.25");
        BigDecimal num2 = new BigDecimal("3.1");

        // 덧셈
        BigDecimal sum = num1.add(num2);

        // 나눗셈 (소수점 자릿수 지정 필요)
        BigDecimal result = num1.divide(num2, 10, BigDecimal.ROUND_HALF_UP); // 소수점 10자리까지 반올림

        System.out.println("합: " + sum);       // 13.35
        System.out.println("나눗셈: " + result); // 3.3064516129
    }
}

주요 메서드

add() 덧셈
subtract() 뺄셈
multiply() 곱셈
divide() 나눗셈 (반드시 소수 자릿수 설정)
setScale() 소수점 자리수 지정
compareTo() 크기 비교 (-1, 0, 1 리턴)
equals() 정확한 값 비교

반올림 모드

  • new BigDecimal(숫자)는 부동소수점 오차가 생길 수 있으므로
    👉 반드시 문자열로 생성하는 게 좋아요: new BigDecimal("1.1")
  • 나눗셈할 때는 소수 자릿수(scale) 와 반올림 방식(rounding mode) 지정 안 하면 ArithmeticException 발생
ROUND_UP 무조건 올림
ROUND_DOWN 무조건 버림
ROUND_HALF_UP 5 이상이면 올림 (일반적인 반올림)
ROUND_HALF_EVEN 오사오입 반올림

float와 double에서 부동소수점 오차가 생기는 이유

  • 컴퓨터가 10진 소수를 정확하게 2진수로 표현하지 못하기 때문
  • 10진수 소수는, 대부분 컴퓨터의 2진수로는 무한 반복 소수
  • 컴퓨터는 메모리의 한계 때문에 이걸 일정 비트 수로 잘라서 저장
  • 근사값만 저장되기 때문에 오차가 발생
0.1(10진수) = 0.000110011001100... (2진수, 무한 반복)

타입크기정밀도(소수점 자리)

float 32비트 약 7자리 정밀도
double 64비트 약 15~16자리 정밀도
public class Main {
    public static void main(String[] args) {
        double a = 0.1;
        double b = 0.2;
        double sum = a + b;

        System.out.println("0.1 + 0.2 = " + sum);
        //0.1 + 0.2 = 0.30000000000000004
    }
}

BigDecimal이 부동소수점 오차를 피할 수 있는 이유

  • 이진법 대신 십진법(10진수)을 기반으로 수를 처리하기 때문
  • BigDecimal 내부에서는 숫자를 다음처럼 문자열처럼 분해해서 저장
  • 이 방식은 정확한 소수값을 표현하고 계산할 수 있게 해줘요.
    이진 부동소수점처럼 무한 반복 표현이 필요 없고, 자리수도 원하는 만큼 지정할 수 있기 때문
BigDecimal value = new BigDecimal("123.456");

BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
BigDecimal result = b1.add(b2);
System.out.println(result); // 0.3

내부적으로는 다음처럼 저장:
정수부: 123456
소수 자릿수(scale): 3 (소수점이 3자리 뒤에 있다는 의미)
즉, 실제 숫자는 (정수부) × 10^(-scale)
→ 123456 × 10^(-3) = 123.456

10진 소수를 2진수 소수로 바꾸는 방법

소수부는 다음 과정을 반복해서 변환합니다:

  1. 10진 소수 × 2
  2. 정수부(0 또는 1)를 기록
  3. 소수부만 남겨서 다시 ×2
  4. 원하는 정밀도까지 반복
1) 0.1  × 2 = 0.2       → 0
2) 0.2  × 2 = 0.4       → 0
3) 0.4  × 2 = 0.8       → 0
4) 0.8  × 2 = 1.6       → 1
5) 0.6  × 2 = 1.2       → 1
6) 0.2  × 2 = 0.4       → 0
7) 0.4  × 2 = 0.8       → 0
8) 0.8  × 2 = 1.6       → 1
9) 0.6  × 2 = 1.2       → 1
10) 0.2 × 2 = 0.4       → 0
...

 

  • 0.1은 2진수로 정확하게 표현 불가능한 무한 반복 소수
  • 그래서 float나 double 같은 2진수 기반 타입으로 표현하면 근사값만 저장됨

10진수2진수 표현정확한 표현 가능 여부

0.5 0.1 가능
0.25 0.01 가능
0.1 0.000110011... 불가능 (무한 반복)
0.2 0.00110011... 불가능
728x90
반응형

'Java' 카테고리의 다른 글

[Java] Jackson Streaming API, TreeModel  (0) 2025.04.19
[Java] BigInteger  (0) 2025.04.19
[Java] DIP (Dependency Inversion Principle)  (0) 2025.03.03
[Java] Bounded WildCard  (1) 2025.03.03
[Java] Generic  (0) 2025.03.03
반응형
300x250