From c88473f0c09cb2ff34d8832ed6c506625dec410e Mon Sep 17 00:00:00 2001 From: Ian Park <117673396+ian9292@users.noreply.github.com> Date: Sun, 3 Sep 2023 12:11:20 +0900 Subject: [PATCH] =?UTF-8?q?[item60]:=20=EC=A0=95=ED=99=95=ED=95=9C=20?= =?UTF-8?q?=EB=8B=B5=EC=9D=B4=20=ED=95=84=EC=9A=94=ED=95=98=EB=8B=A4?= =?UTF-8?q?=EB=A9=B4=20float=EC=99=80=20double=EC=9D=80=20=ED=94=BC?= =?UTF-8?q?=ED=95=98=EB=9D=BC=20(#141)(=EA=B1=B4=ED=98=B8)=20(#151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Item03]: private 생성자나 열거 타입으로 싱글턴임을 보증하라 (#5)(건호) * docs: chore * [Item08]: finalizer와 cleaner 사용을 피하라 (#13)(건호) * [Item29] 이왕이면 제네릭 타입으로 만들라 (#23)(건호) * [item42]: 익명 클래스보다는 람다를 사용하라 (#33) * [item47]: 반환타입으로는 스트림보다 컬렉션이 낫다 * prep: item 81 * [Item81]: wait와 notify는 동시성 유틸리티를 애용하라 (#53) (건호) * chore wait와_notify보다는_동시성_유틸리티를_애용하라.md * [item86]: Serializable을 구현할지는 신중히 결정하라 (#63) * [Item15]: 클래스와 멤버의 접근 권한을 최소화하라 (#80)(건호) * [Item10]: equals는 일반 규약을 지켜 재정의하라 (#73)(건호) * [item60]: 정확한 답이 필요하다면 float와 double은 피하라 (#141)(건호) --------- Co-authored-by: Gunho Park Co-authored-by: devgun <117673396+devgun3123@users.noreply.github.com> Co-authored-by: Gunho Park <41619898+gunh0@users.noreply.github.com> --- ...0_\355\224\274\355\225\230\353\235\274.md" | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 "Ch09/item60/\354\240\225\355\231\225\355\225\234_\353\213\265\354\235\264_\355\225\204\354\232\224\355\225\230\353\213\244\353\251\264_float\354\231\200_double\354\235\200_\355\224\274\355\225\230\353\235\274.md" diff --git "a/Ch09/item60/\354\240\225\355\231\225\355\225\234_\353\213\265\354\235\264_\355\225\204\354\232\224\355\225\230\353\213\244\353\251\264_float\354\231\200_double\354\235\200_\355\224\274\355\225\230\353\235\274.md" "b/Ch09/item60/\354\240\225\355\231\225\355\225\234_\353\213\265\354\235\264_\355\225\204\354\232\224\355\225\230\353\213\244\353\251\264_float\354\231\200_double\354\235\200_\355\224\274\355\225\230\353\235\274.md" new file mode 100644 index 0000000..8de1d43 --- /dev/null +++ "b/Ch09/item60/\354\240\225\355\231\225\355\225\234_\353\213\265\354\235\264_\355\225\204\354\232\224\355\225\230\353\213\244\353\251\264_float\354\231\200_double\354\235\200_\355\224\274\355\225\230\353\235\274.md" @@ -0,0 +1,91 @@ +# Item 60. 정확한 답이 필요하다면 float와 double은 피하라 + +### float과 double의 문제 + +- float과 double의 연산은 정확하지 않다. + +```java + @Test + void 부동소수점_연산은_정확하지_않음 (){ + double result = 1.03 - 0.42; + System.out.println(result); // 0.6100000000000001 + assertThat(result).isNotEqualTo(0.61); + } +``` + +- 1.03 - 0.42와 같은 단순한 계산에서도 오차가 발생한다. +- 그 이유는 float과 double은 부동소수점 방식을 사용하고 있기 때문이다. + +
+ +### 고정소수점과 부동소수점 + +- 컴퓨터는 숫자를 2진수로 표현한다. +- 이 때, 정수를 2진수로 표현하는 것은 문제가 없다. +- 하지만 소수점을 포함한 실수를 표현할 때는 상황이 조금 달라진다. +- 소수점의 위치를 표현하고, 무엇이 정수 부분이고 무엇이 실수 부분인지 구분해야 하기 때문이다. +- 컴퓨터는 이를 위해 고정소수점 방식과 부동소수점 방식을 사용한다. + +
+ +### 올바른 방안 + +- 위 같은 문제를 올바르게 해결하기 위해서는 정수를 이용하여 실수를 표현한 `BigDecimal`이나 `int`, `long`을 사용해야 한다. + +**BigDecimal** + +Java에는 정수를 이용하여 실수를 표현하기 위한 `BigDecimal`이 제공된다. + +- 아래는 BigDecimal의 실제 내부 구현 중 일부분 이다. + +```java +public class BigDecimal extends Number implements Comparable { +... +private final BigInteger intVal; +private final int scale; +private transient int precision; +... +} +``` + +- private final BigInteger intVal: 정수를 저장하는데 사용한다. +- private final int scale: 총 소수점 자리수를 가리킨다. +- private transient int precision: 정밀도이다. 수가 시작하는 위치부터 끝나는 위치까지 총 자리수이다. +- 위 같은 내부 변수를 활용하여 실수를 정확하게 표현한다. + +```java + @Test + void BigDecimal_사용(){ + BigDecimal result = new BigDecimal("1.03").subtract(new BigDecimal("0.42")); + System.out.println(result); // 0.61 + assertThat(result).isEqualTo(new BigDecimal("0.61")); + } +``` + +- 위 테스트는 정확하게 일치하여 성공한다. + +
+ +**int 혹은 long** + +BigDecimal의 대안으로 int 혹은 long 타입을 사용할 수 있다. 하지만 다룰 수 있는 값의 크기가 제한되며 소수점이 필요한 경우 따로 관리해야 한다. + +**int** + +- 4 byte +- 저장 가능 범위: –2,147,483,648 ~ 2,147,483,647 +- 만약 다뤄야 하는 숫자가 9자리 이하이면 int의 사용을 고려할 수 있다. + +**long** + +- 8 byte +- 저장 가능 범위: -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 +- 만약 다뤄야 하는 숫자가 18자리 이하이면 long의 사용을 고려할 수 있다. + +
+ +### 정리 + +- 정확한 답이 필요한 경우 float나 double을 피해야 한다. +- 소수점 관리 및 성능 저하를 신경 쓰지 않는 다면 BigDecimal의 사용은 좋은 대안이 된다. +- 하지만 성능이 중요하고 숫자가 너무 크지 않으며 소수점을 직접 관리할 자신이 있다면 int나 long 사용을 고려할 수 있다.