Backend/Java

[Effective Java : Item 60] 정확한 답이 필요하다면 float와 double은 피하라

김세진 2024. 3. 21. 00:15
반응형

 

 

 

개요

 

float와 double은 부동소수점 숫자를 표현되는 데 사용되는 데이터 형식으로, 넓은 범위의 수를 '근사치'로 빠르게 계산할 수 있다. 부동 소수점 형식은 유한한 비트 수로 실수를 표현하는데, 이로 인해 정밀도 손실이 발생하기 때문이다. 따라서 아주 정확한 계산이 필요한 금융 관련 계산에는 절대 사용하면 안 된다. 다음의 예제를 살펴보자.

 

System.out.println("1.03 - 0.42 = " + (1.03 - 0.42));

 

1.03 달러에서 42센트를 사용한 예제이다. 이를 실행하면 다음과 같은 결과를 얻는다.

 

 

기대한 값인 0.61이 아닌 0.6100000000000001 을 출력한다. 심지어 특수한 사례도 아니다. 좀 더 구체적인 사례로 살펴보자.

 

double funds = 1.00;
int itemBought = 0;
for (double price = 0.10; funds >= price; price += 0.10) {
    funds -= price;
    itemBought++;
}
System.out.println(itemBought + "개 구입");
System.out.println("잔돈(달러): " + funds);

 

사탕 가격이 10센트씩 증가하는 형태로 선반에 있고, 수중에 1달러가 있을 때 몇 개까지 살 수 있는지를 출력하는 코드이다. 정상적이라면 4개를 구매할 수 있고, 잔돈은 0달러가 될 것이다. 하지만 이를 실행하면 다음과 같은 결과가 나온다.

 

 

위와 마찬가지로 잘못된 결과가 나온다. 이를 해결하기 위해서는 금융 계산에는 BigDecimal, int 혹은 long을 사용해야 한다.

 

 


BigDecimal

 

final BigDecimal TEN_CENTS = new BigDecimal(".10");

int itemBought = 0;
BigDecimal funds = new BigDecimal("1.00");
for (BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0; price = price.add(TEN_CENTS)) {
    funds = funds.subtract(price);
    itemBought++;
}
System.out.println(itemBought + "개 구입");
System.out.println("잔돈(달러): " + funds);

 

BigDecimal로 변환한 코드이다. 이를 실행하면 아래와 같은 결과가 나온다.

 

 

올바른 결과가 나오는 것을 확인할 수 있다. 하지만 BigDecimal은 기본 타입보다 쓰기가 훨씬 불편하고, 느리다는 단점이 있다. 계산이 복잡하지 않은 경우 성능은 크게 문제가 되지 않지만 사용하기 불편하단 문제는 여전히 남아있게 된다.

 

 


int & long

 

BigDecimal의 대안으로 int 혹은 long 타입을 사용할 수도 있다. 하지만 이 역시 단점이 존재하는데, 다룰 수 있는 크기가 제한되고 소수점을 직접 관리해야 한다. 하지만 BigDecimal과 달리 성능을 챙길 수 있다. 예시의 모든 계산을 달러 대신 센트로 수행할 경우 이 방법을 간편하게 적용할 수 있다.

 

public static void main(String[] args) {
    int itemBought = 0;
    int funds = 100;
    for (int price = 10; funds >= price; price += 10) {
        funds -= price;
        itemBought++;
    }

    System.out.println(itemBought + "개 구입");
    System.out.println("잔돈(센트): " + funds);
}

 

 


정리

 

1. 정확한 답이 필요한 계산에는 float 혹은 double을 사용하면 안 된다.

2. 사용상의 불편함이나 성능 저하를 상관하지 않는다면 BigDecimal을 사용하자. 여덟 가지 반올림 모드도 제공하기 때문에 법으로 정해진 반올림을 수행해야 하는 비즈니스 계산에서도 유용하다.

3. 성능이 중요하고 소수점을 직접 추적할 수 있으며, 숫자가 너무 크지 않다면 int 혹은 double을 사용하자.

 

 

 

 

 

반응형