일상/레벨업 독서

[이펙티브 자바] Item 60 정확한 답이 필요하다면 float와 double은 피하라.

Gamii 2022. 5. 16. 23:11
728x90
핵심 정리

정확한 답이 필요한 계산에는 float나 double을 피하라. 소수점 추적은 시스템에 맡기고, 코딩 시의 불편함이나 성능 저하를 신경 쓰지 않겠다면 BigDecimal을 사용하라. BigDecimal이 제공하는 여덟 가지 반올림 모드를 이용하여 반올림을 완벽히 제어 할 수 있다. 법으로 정해진 반올림을 수행해야 하는 비즈니스 계산에서 아주 편리한 기능이다. 반면, 성능이 중요하고 소수점을 직접 추적할 수 있고 숫자가 너무 크지 않다면 int나 long을 사용하라. 숫자를 아홉 자리 십진수로 표현할 수 있다면 int를 사용하고, 열여덟 자리 십진수로 표현할 수 있다면 long을 사용하라. 열여덟 자리를 넘어가면 BigDecimal을 사용해야 한다. 

 

 

float와 double 타입은 금융 계산시 부정확하다.

 

자바에서 제공하는 실수형 데이터 타입인 float와 double은 과학과 공학 계산용으로 설계되었다. 이진 동소수점 연산에 쓰이며, 넓은 범위의 수를 빠르게 정밀한 '근사치'로 계산하도록 세심하게 설계되었다. 따라서 정확한 결과가 필요할 때는 사용하면 안된다. float와 double 타입은 특히 금융 관련 계산과 맞지 않는다. 0.1 혹은 10의 음의 거듭 제곱 수를 표현 할 수 없기 때문이다.

 

 

public static void main(String[] args){
    double funds = 1.00;
    int itemBought = 0;
    for(double price = 0.10; funds >= price; price +=0.10){
        funds -= price;
        itemsBought++;
    }
    
    //잔돈은 0.3999999999999999달러가 남는다.
    //잘못된 결과이다.
    System.out.println(itemsBought + "개 구입");
    System.out.println("잔돈(달러)" + funds);
}

 

 

 

참고) 부동소수점이란?

https://madplay.github.io/post/the-need-for-bigdecimal-in-java

 

 

 

 

 

 

해결책 : BigDecimal, int, long을 사용하자.

 

 

1. BigDecimal 사용시

public static void main(String[] args) {
    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);
        itemBount++;
    }
}

BigDecimal 은 기본타입보다 사용하기가 불편하고, 무겁고 느리다.

 

참고) BigDecimal 타입 사용시 문자열을 넘기고, double을 넘기면 안된다. double을 넘기면서 그 순간 정확도를 보장하지 못한다.

 

 

2. int 사용시

public static void main(String[] args) {
    public static void main(String[] args) {
        int itemsBought = 0;
        int funds = 100;
        for (int price = 10; funds >= price; price += 10) {
            funds -= price;
            itemsBought++;
        }
        System.out.println(itemsBought + "개 구입");
        System.out.println("잔돈(센트): " + funds);
    }
}

BigDecimal 대신 int나 long 타입을 사용시 다룰 수 있는 값의 크기가 제한되고, 소수점을 직접 관리해야한다.