개요
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을 사용하자.
'Dev > Java' 카테고리의 다른 글
[Effective Java : Item 77] 예외를 무시하지 말라 (0) | 2024.04.01 |
---|---|
[Effective Java : Item 69] 예외는 진짜 예외 상황에만 사용하라 (2) | 2024.03.29 |
[Effective Java : Item 59] 라이브러리를 익히고 사용하라 (0) | 2024.03.20 |
[Effective Java : Item 58] 전통적인 for 문보다는 for-each 문을 사용하라 (0) | 2024.03.18 |
[Effective Java : Item 55] 옵셔널 반환은 신중히 하라 (0) | 2024.03.14 |