맞는데 왜 틀릴까..?

Java/Effective Java

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

안도일 2023. 7. 23. 17:19

 

float와 double의 문제점

 

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

 

 

ex1) 주머니에 1.03 달러가 있는데 그중 42 센트를 썼다.

System.out.println(1.03 - 0.42)는 0.6100000000000001을 출력한다.

 

 

ex2) 주머니에 1 달러가 있는데 10센트 사탕 9개를 샀다.

System.out.println(1.00 - 9*0.10)는 0.09999999999999998을 출력한다.

 

 

ex3) 주머니에 1달러가 있고, 선반에 10센트부터 1 달러 짜리 사탕이 있을 때 10센트짜리부터 하나씩 살 수 있을 때까지 사는 프로그램을 만들어보자

 

금융 계산에 부동소수 타입 사용 

 

 

  • 정답은 사탕 4개를 구입하고 잔돈이 0원이지만, 사탕 3개를 구입한 후 잔돈이 0.39999999999.. 달러로 출력된다.

 

 

 

BigDecimal, int, long을 이용한 해법

 

금융 계산에는 BigDecimal, int 혹은 long을 사용해야 한다.

 

 

BigDecimal을 사용한 해법

 

 

  • BigDecimal 생성자 중 문자열을 받는 생성자는 계산 시 부정확한 값이 사용되는 것을 막기 위한 조치다.
  • 사탕 4개를 구입한 후 잔돈 0달러가 남는다는 정답이 도출된다.
  • 기본 타입보다 쓰기 훨씬 불편하고, 훨씬 느리다.

 

 

정수 타입을 사용한 해법

 

 

  • 모든 계산을 달러 대신 센트로 수행한다.
  • 다룰 수 있는 값의 크기가 제한되고, 소수점을 직접 관리해야 한다.

 

결론

 

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