티스토리 뷰
(Effective Java) 규칙 60. 정확한 답이 필요하다면 float과 double은 피하라
float과 double의 설계
-
float
과double
타입을 과학과 공학 계산용으로 설계되었다.- 이진 부동소수점 연산에 쓰이며, 넓은 범위의 수를 빠르게 정밀한 '근사치'로 계산하도록 세심하게 설계되었다.
- 따라서 정확한 결과가 필요할 때는 사용하면 안된다.
-
float
와double
타입은 특히 금융 관련 계산과는 맞지 않는다.- 0.1 혹은 10의 음의 거듭 제곱 수(10^-1, 10^-2 등)를 표현할 수 없기 때문임
float과 double 사용의 문제
-
예) 주머니에 1.03달러가 있었는데 그중 42센트를 썼다면 남은 돈은 얼마인가?
//답을 구하는 어설픈 코드 .. System.out.println(1.03 - 0.42);
- 안타깝게도 이 코드는
0.6100000000000001
을 출력하게 됨 (이는 특수한 사례도 아님)
- 안타깝게도 이 코드는
-
예) 주머니에 1달러가 있었는데 10센트짜리 사탕9개를 샀다고 했을 때 남은 돈은 얼마인가?
//답을 구하는 어설픈 코드 .. System.out.println(1.00 - 9 * 0.10);
- 이 코드는
0.09999999999999998
을 출력하게 됨 - 결괏값을 출력하기 전에 반올림하면 해결되리라 생각할지 모르지만, 틀린 답이 나올 수 있다.
- 이 코드는
-
예) 주머니에 1달러가 있고, 선반에 10, 20센트, ... 1달러짜리의 사탕이 놓여 있을 때 사탕을 살수 있는 개수와, 잔돈은 얼마나 될까?
//오류 발생! 금융 계산에 부동소수 타입을 사용했다. public static void main(String[] args) { double funds = 1.00; int itemsBought = 0; for (double price = 0.10; funds >= price; price += 0.10) { funds -= price; itemsBought++; } System.out.println(itemsBought + "개 구입"); System.out.println("잔돈(달러):" + funds); }
- 실행을 해보면 사탕 3개를 구입한 후 잔돈은
0.3999999999999999
달러가 남았음을 알게 됨
- 실행을 해보면 사탕 3개를 구입한 후 잔돈은
해결 방법
-
금융 계산에는
BigDecimal
,int
혹은long
을 사용해야 한다. -
앞선 코드에서
double
타입을BigDecimal
로 교체만 했다.BigDecimal
의 생성자 중 문자열을 받는 생성자를 사용했을에 주목할 것public static void main(String[] args) { final BigDecimal TEN_CENTS = new BigDecimal(".10"); int itemsBought = 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); itemsBought++; } System.out.println(itemsBought + "개 구입"); System.out.println("잔돈(달러):" + funds); }
- 실행하면 사탕 4개를 구입한 후 잔돈은 0달러가 남게 됨
-
하지만
BigDecimal
에는 기본 타입보다 쓰기가 훨씬 불편하고, 훨씬 느리다는 두 가지 단점이 있음- 단발성 계산이라면 느리다는 문제는 무시할 수 있지만, 쓰기 불편하다는 점은 못내 아쉬울 것임
-
BigDecimal
의 대안으로int
혹은long
타입을 쓸 수도 있다.- 그럴 경우 다룰 수 있는 값의 크기가 제한되고, 소수점을 직접 관리해야 함
int
방식으로 구현해본 예제public static void main(String[] args) { final BigDecimal TEN_CENTS = new BigDecimal(".10"); 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); }
결론
-
정확한 답이 필요한 계산에는
float
이나double
을 피하라. -
소수점 추적은 시스템에 맞기고, 코딩 시에 불편함이나 성능 저하를 신경 쓰지 않겠다면
BigDecimal
을 사용하라.BigDecimal
이 제공하는 여덟가지 반올림 모드를 이용하여 반올림을 완벽히 제어할 수 있음
-
반면, 성능이 중요하고 소수점을 직접 추적할 수 있고 숫자가 너무 크지 않다면
int
나double
을 사용하라.- 숫자를 아홉 자리 십진수로 표현할 수 있다면
int
를, 열여덟 자리 십진수로 표현할 수 있다면long
을 사용 - 이를 넘어가면
BigDecimal
을 사용
- 숫자를 아홉 자리 십진수로 표현할 수 있다면
끝으로
이 글이 도움이 되었다면, 하단의 Google 광고 👎👎👎 한번씩 클릭 부탁 드립니다. 🙏🙏🙏
광고 클릭은 많은 힘이 됩니다!
반응형
'프로그래밍 > EffectiveJava' 카테고리의 다른 글
(이펙티브 자바) 규칙45. 스트림은 주의해서 사용하라 (0) | 2020.03.05 |
---|---|
(이펙티브 자바) 규칙15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2020.03.03 |
(이펙티브 자바) 규칙58. 전통적인 for 문보다는 for-each 문을 사용하라 (0) | 2020.02.29 |
(이펙티브 자바) 규칙57. 지역변수의 범위를 최소화하라 (0) | 2020.02.28 |
(이펙티브 자바) 규칙16. public 클래스에서는 public필드가 아닌 접근자 메서드를 사용하라 (0) | 2020.02.26 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 이펙티브자바
- 스프링부트
- TableView
- intelij
- java
- springboot
- 자바
- JavaFX 테이블뷰
- Java UI
- 배낭 여행
- 일본여행
- 자전거
- 텐트
- 일본 자전거 여행
- 이펙티브
- 배낭여행
- 일본 여행
- JavaFX 종료
- 자전거 여행
- git
- JavaFX Window Close
- windows
- JavaFX Table View
- 일본 배낭여행
- 방통대 과제물
- 이펙티브 자바
- JavaFX
- effective java
- 인텔리제이
- effectivejava
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함