실수 값 계산에 대해 고민하다가, BigDecimal에 대해 정리를 해봐야겠다는 생각이 들었다.
컴퓨터가 2진수를 이용해 실수를 저장하는 방법 중 하나.
Java에서 일반적으로 실수를 저장하기위해서 float와 double 타입을 쓴다.
두 자료형 모두 부동소수점 방식으로 수를 저장한다.
하지만 부동소수점 방식으로는 10진수를 정확하게 저장할 수 없다.
@Test
void test(){
double number1 = 10.0000000004;
double number2 = 100.0000000005;
assertThat(number2-number1).isEqualTo(90.0000000001);
}
위의 테스트를 실행하면 계산값이 90.00000000009999가 나와서 실패한다.
부동소수점은 10진수를 2진수로 바꾸면서 근사치로 저장하기 때문이다.
위의 float와 double에서 오차범위를 넘어가면 실수 계산에서 오차가 발생한다.
돈을 계산하거나 더 정확한 계산이 필요한 경우 부동소수점 방식으로는 무리가 있다.
그래서 Java에서는 정확한 실수의 계산을 위해 BigDecimal 클래스를 제공한다.
Java에서 숫자를 오차없이 정밀하게 저장할 수 있는 클래스
import java.math.BigDecimal;
10진수를 정확하게 표현할 수 있다.
불변성(immutable)을 가진다. 한번 생성하면 내부 값이 변경되지 않는다.
덧셈, 뺄셈, 곱셈, 나눗셈, 반올림, 비교 등 다양한 연산을 지원한다.
원하는 소수 자릿수를 지정해 숫자를 표현할 수 있다.
float가 4바이트, double이 8바이트인 반면 BigDecimal은 기본적으로 16바이트 이상의 메모리를 차지한다.(구현에 따라 더 차지할 수 있다.)
객체 생성과 메모리 사용, 연산의 복잡성 등의 이유로 float, double보다 성능적인 면에서 느리다.
BigDecimal number3 = new BigDecimal("0.01");
BigDecimal.ZERO
BigDecimal.ONE
BigDecimal.TEN
String 값으로 초기화하는 것이 가장 좋다.
double로 초기화를 하면 double에 저장된 2진수 근사치로 저장되기때문이다.
0,1,10은 미리 정의되어있다.
BigDecimal a = new BigDecimal("3.14");
BigDecimal b = new BigDecimal("3.140");
a == b; // false. 객체이기때문에 ==으로 비교는 메모리주소상의 비교이다.
a.equals(b); // false. 소수점 맨 뒤의 0까지 동일해야 true를 반환한다.
a.compareTo(b); // 0. 크기만 비교했을 때 동일하면 0, 적으면 -1, 크면 1을 반환한다.
a.add(b); // 더하기
a.subtract(b); // 빼기
a.multiply(b); // 곱하기
a.divide(b); // 나누기
a.remainder(b); // 나머지연산
a.abs(); // 절대값
RoundingMode | 설명 |
---|---|
CEILING | 올림 |
FLOOR | 내림 |
UP | 양수일 때는 올림, 음수일 때는 내림 |
DOWN | 양수일 때는 내림, 음수일 때는 올림 |
HALF_UP | 반올림 (5 이상 올림, 5 미만 내림) |
HALF_EVEN | 반올림 (반올림 자리의 값이 짝수면 HALF_DOWN, 홀수면 HALF_UP) |
HALF_DOWN | 반올림 (6 이상 올림, 6 미만 내림) |
UNNECESSARY | 나눗셈의 결과가 딱 떨어지는 수가 아니면 ArithmeticException 발생 |
BigDecimal c = new BigDecimal("10");
BigDecimal d = new BigDecimal("3");
c.divide(d); // 나누기 결과가 무한으로 떨어져 ArithmeticException 발생
c.divide(d, RoundingMode.HALF_EVEN); // 3
c.divide(d, 5, RoundingMode.HALF_EVEN); // 3.33333 반올림 자리를 명시