float와 double 타입은 과학과 공학 계산용으로 설계되었다. 이전 부동소수점 연산에 쓰이며, 넓은 범위의 수를 빠르게 정밀한 ‘근사치’로 계산하도록 설계되었다. 따라서 정확한 결과가 필요할 때는 사용하면 안 된다. float와 double 타입은 특히 금융 관련 계산과는 맞지 않는다. 0.1 혹은 10의 음의 거듭 제곱 수를 표현할 수 없기 때문이다.
- Effective Java Item. 60
float
와 double
타입은 각각 4바이트 와 8바이트의 메모리 크기를 가지는 타입이다.
- float: 1개의 부호, 8개의 지수, 23개의 소수 비트를 가진다.
- double: 1개의 부호, 11개의 지수, 52개의 소수 비트를 가진다.
즉, 제한된 메모리 내에서 소수를 표현해야 하기 때문에 특정 소수를 표현할 수 없다. 이는 소수점 계산에서 오차를 발생시키고, 프로그램이 정상적으로 동작하지 않게 되는 경우가 발생할 수도 있다.
따라서 JAVA는 이를 해결하고자 십진 부동소수점을 사용하는 BigDecimal
클래스를 지원한다.
생성
생성자로 double 타입을 넘겨주는 경우 연산 결과가 정확하지 않을 수도 있다. 2진 부동소수점으로 표현할 수 없는 수는 근사치로 저장되기 때문에 이를 생성자로 넘겨주는 경우 BigDecimal은 이'근사치'를 정확하게 저장한다.
또, BigDecimal의 생성자를 살펴보면 아주 많은 연산을 수행하기 때문에, 인스턴스 생성 속도가 느리다.
따라서 BigDecimal은 이 모두를 해결할 수 있는 valueOf 정적 팩토리 메서드를 지원한다.
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
만약 valueOf를 통해 BigDecimal 인스턴스를 얻으려는 경우 파라매터의 값에 해당하는 BigDecimal 인스턴스가 클래스 캐시에 존재한다면 이를 캐싱하여 반환한다. 또한 Double 클래스를 통해 2진 부동소수로 표현된 값을 10진 문자열로 변환하고 이를 통해 BigDecimal 인스턴스를 생성한다.
예를 들어 아래의 두 BigDecimal 객체는 비교에 안전하고 논리적으로 동등하다.
public static void main(String[] args) throws IOException {
BigDecimal test1 = new BigDecimal(Double.toString(0.10));
BigDecimal test2 = BigDecimal.valueOf(0.10);
System.out.println(test1.scale()); // 1
System.out.println(test2.scale()); // 1
System.out.println(test1.equals(test2)); // true
}
생성자를 통해 BigDecimal 인스턴스를 생성하려는 경우 String을 넘겨주도록 하고, 소수 값을 넘겨줘야 하는 경우에는 ValueOf를 사용하자.
Equals와 compareTo
BigDecimal의 equalsd와 compareTo는 내부적으로 다르게 동작한다. equals는 객체가 논리적으로 동등한지를 판단한다. 즉, 값 뿐만 아니라 scale까지 비교를 진행한다. compareTo는 정수부, 소수부가 같다면 scale은 비교대상에 두지 않는다. 즉, 값만을 가지고 비교할 때 사용한다.
BigDecimal test1 = new BigDecimal(0.10);
BigDecimal test2 = new BigDecimal("0.10");
System.out.println(test1.equals(test2)); // false
System.out.println(test1.compareTo(test2) == 0); // false
System.out.println(test1.scale()); // 55
System.out.println(test2.scale()); // 2
test1은 0.10은 생성자로 부동 소수점을 넘겨준다. 따라서 0.10의 근사치를 가지게 된다. scale을 출력해보면 55가 출력된다. test2는 문자열 리터럴 0.10을 생성자로 넘겨주게된다 이는 정확한 0.10을 표현한다. 따라서 두 BigDecimal은 논리적으로 동등하지 않으며, 값이 동일하지도 않다.
BigDecimal test1 = BigDecimal.valueOf(0.10); // scale 1
BigDecimal test2 = new BigDecimal("0.10"); // scale 2
System.out.println(bc.equals(bd)); // false
System.out.println(bc.compareTo(bd) == 0); // true
ValueOf로 값을 넘겨준경우 정확한 0.1이 십진 부동소수점으로 저장되고, 문자열의 경우 0.10이 저장된다. 따라서 equals를 통해 비교한 경우 scale값이 다르니 false, compareTo의 경우 정수부와 소수부가 동일하여 true라는 결과가 나온다.
값을 비교하고 싶을 때는 compareTo를 사용하고, 두 객체가 동등한지 판단하고 싶을 때는 equals를 사용하자.
'JAVA' 카테고리의 다른 글
디자인 패턴 - Adapter (0) | 2024.01.19 |
---|---|
SOLID (0) | 2024.01.18 |
데몬 스레드 (Daemon Thread) (2) | 2024.01.01 |
GC 짚고 넘어가기 (1) | 2023.12.29 |
[JAVA 객체지향] 비즈니스 로직과 View의 의존성 배제하기 (0) | 2023.11.02 |
float와 double 타입은 과학과 공학 계산용으로 설계되었다. 이전 부동소수점 연산에 쓰이며, 넓은 범위의 수를 빠르게 정밀한 ‘근사치’로 계산하도록 설계되었다. 따라서 정확한 결과가 필요할 때는 사용하면 안 된다. float와 double 타입은 특히 금융 관련 계산과는 맞지 않는다. 0.1 혹은 10의 음의 거듭 제곱 수를 표현할 수 없기 때문이다.
- Effective Java Item. 60
float
와 double
타입은 각각 4바이트 와 8바이트의 메모리 크기를 가지는 타입이다.
- float: 1개의 부호, 8개의 지수, 23개의 소수 비트를 가진다.
- double: 1개의 부호, 11개의 지수, 52개의 소수 비트를 가진다.
즉, 제한된 메모리 내에서 소수를 표현해야 하기 때문에 특정 소수를 표현할 수 없다. 이는 소수점 계산에서 오차를 발생시키고, 프로그램이 정상적으로 동작하지 않게 되는 경우가 발생할 수도 있다.
따라서 JAVA는 이를 해결하고자 십진 부동소수점을 사용하는 BigDecimal
클래스를 지원한다.
생성
생성자로 double 타입을 넘겨주는 경우 연산 결과가 정확하지 않을 수도 있다. 2진 부동소수점으로 표현할 수 없는 수는 근사치로 저장되기 때문에 이를 생성자로 넘겨주는 경우 BigDecimal은 이'근사치'를 정확하게 저장한다.
또, BigDecimal의 생성자를 살펴보면 아주 많은 연산을 수행하기 때문에, 인스턴스 생성 속도가 느리다.
따라서 BigDecimal은 이 모두를 해결할 수 있는 valueOf 정적 팩토리 메서드를 지원한다.
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
만약 valueOf를 통해 BigDecimal 인스턴스를 얻으려는 경우 파라매터의 값에 해당하는 BigDecimal 인스턴스가 클래스 캐시에 존재한다면 이를 캐싱하여 반환한다. 또한 Double 클래스를 통해 2진 부동소수로 표현된 값을 10진 문자열로 변환하고 이를 통해 BigDecimal 인스턴스를 생성한다.
예를 들어 아래의 두 BigDecimal 객체는 비교에 안전하고 논리적으로 동등하다.
public static void main(String[] args) throws IOException {
BigDecimal test1 = new BigDecimal(Double.toString(0.10));
BigDecimal test2 = BigDecimal.valueOf(0.10);
System.out.println(test1.scale()); // 1
System.out.println(test2.scale()); // 1
System.out.println(test1.equals(test2)); // true
}
생성자를 통해 BigDecimal 인스턴스를 생성하려는 경우 String을 넘겨주도록 하고, 소수 값을 넘겨줘야 하는 경우에는 ValueOf를 사용하자.
Equals와 compareTo
BigDecimal의 equalsd와 compareTo는 내부적으로 다르게 동작한다. equals는 객체가 논리적으로 동등한지를 판단한다. 즉, 값 뿐만 아니라 scale까지 비교를 진행한다. compareTo는 정수부, 소수부가 같다면 scale은 비교대상에 두지 않는다. 즉, 값만을 가지고 비교할 때 사용한다.
BigDecimal test1 = new BigDecimal(0.10);
BigDecimal test2 = new BigDecimal("0.10");
System.out.println(test1.equals(test2)); // false
System.out.println(test1.compareTo(test2) == 0); // false
System.out.println(test1.scale()); // 55
System.out.println(test2.scale()); // 2
test1은 0.10은 생성자로 부동 소수점을 넘겨준다. 따라서 0.10의 근사치를 가지게 된다. scale을 출력해보면 55가 출력된다. test2는 문자열 리터럴 0.10을 생성자로 넘겨주게된다 이는 정확한 0.10을 표현한다. 따라서 두 BigDecimal은 논리적으로 동등하지 않으며, 값이 동일하지도 않다.
BigDecimal test1 = BigDecimal.valueOf(0.10); // scale 1
BigDecimal test2 = new BigDecimal("0.10"); // scale 2
System.out.println(bc.equals(bd)); // false
System.out.println(bc.compareTo(bd) == 0); // true
ValueOf로 값을 넘겨준경우 정확한 0.1이 십진 부동소수점으로 저장되고, 문자열의 경우 0.10이 저장된다. 따라서 equals를 통해 비교한 경우 scale값이 다르니 false, compareTo의 경우 정수부와 소수부가 동일하여 true라는 결과가 나온다.
값을 비교하고 싶을 때는 compareTo를 사용하고, 두 객체가 동등한지 판단하고 싶을 때는 equals를 사용하자.
'JAVA' 카테고리의 다른 글
디자인 패턴 - Adapter (0) | 2024.01.19 |
---|---|
SOLID (0) | 2024.01.18 |
데몬 스레드 (Daemon Thread) (2) | 2024.01.01 |
GC 짚고 넘어가기 (1) | 2023.12.29 |
[JAVA 객체지향] 비즈니스 로직과 View의 의존성 배제하기 (0) | 2023.11.02 |