최근에 금융쪽 도메인에서 개발 업무를 하는 친구한테 돈을 계산할때 자료형으로 double, float보다 BigDecimal이라는 자료형을 사용한다고 들었습니다. 그 이유를 물어보니 위 자료형(double, float)보다 BigDecimal이 미세한 숫자의 변동도 허용하지 않기 때문이라는 사실을 알게 되었습니다.
먼저, float과 double 타입의 표현 범위를 알아보았습니다.
| Type | 범위 |
|---|---|
| float | 1.4E-45 ~ 3.4028235E38 |
| double | 4.9E~324 ~ 1.7976931348623157E308 |
소수점을 저장할 수 있는 타입인 float과 double은 소수점의 정밀도가 완벽하지 않아 값의 오차가 생길 수 있습니다. 소수점 이하의 수를 다룰 때 double과 float은 사칙연산 시 정확한 값을 출력하지 않을 수 있습니다. 그 이유는 내부적으로 수를 저장할 때 이진수의 근사치를 저장하기 때문입니다. BigDecimal은 이러한 문제점을 해결해주는 자료형입니다. 하지만 속도가 위 자료형들보다는 느리다는 단점이 있지만, 정확한 소수점을 다룰려면 사용해야 됩니다.
precision: 숫자를 구성하는 전체 자리수라고 생각하면 편하나, 정확하게 풀이하면 왼쪽이 0이 아닌 수가 시작하는 위치부터 오른쪽이 0이 아닌 수로 끝나는 위치까지의 총 자리수 입니다. unscale과 동의어 입니다.
scale: 전체 소수점 자리수라고 생각하면 편하나, 정확하게 풀이하면 소수점 첫째 자리부터 오른쪽부터 0이 아닌 수로 끝나는 위치까지의 총 소수점 자리수입니다. fraction과 동의어입니다.
BigDecimal은 32bit의 소수점 크기를 가집니다.DECIMAL128: IEEE 754-2008에 의해 표준화된, 부호와 소수점을 수용하며, 최대 34자리까지 표현 가능한 10진수를 저장할 수 있는 형식입니다
극단적으로 미국 정부의 총 부채액이 15조 7천 500억 달러로 총 14자리임을 감안하면, 금융권에서 처리되는 대부분의 금액을 수용할 수 있는 크기입니다. Java에서는 BigDecimal 타입을 공식적으로 지원합니다.
public class BigDecimalExam {
public static void main(String[] args) {
BigDecimal bigDecimal = new BigDecimal("10000.12345");
System.out.println(bigDecimal);
}
}
BigDecimal은 java.math 패키지 안에 존재하며 위와 같이 선언합니다. double 타입으로 초기화를 해줘도 되지만 기대값과 다르기 때문에 일반적으로 문자열로 인자 값을 넘겨줘야 의도한 값이 출력됩니다.
BigDecimal의 생성자를 호출 할때 BigDecimal.valueOf()를 사용해서 객체를 생성할 수 있는데, 내부적으로 0부터 10까지 캐싱을 하고 있기 때문에 new BigDecimal("123.9")보다 성능이 더 좋습니다. 또한 double 실수 값을 생성자로 생성해도 연산결과가 정확하지 않을 수 있으니, valueOf()를 사용하여 생성하는 것이 좋다고 합니다.
BigDecimal은 문자열 타입이기 때문에 Integer 타입처럼 사칙연산이 불가능합니다. 그래서 내부적으로 정의되어 있는 메서드를 통해서 사칙연산을 수행합니다.
public class BigDecimalExam {
public static void main(String[] args) {
BigDecimal number1 = new BigDecimal("10000.12345");
BigDecimal number2 = new BigDecimal("10000");
System.out.println(number1.add(number2));
}
}
20000.12345
0.12345
100001234.50000
1.000012345
0.12345
public class BigDecimalExam {
public static void main(String[] args) {
BigDecimal bigDecimal = new BigDecimal("10000.12345");
int intNum = bigDecimal.intValue();
long longNum = bigDecimal.longValue();
float floatNum = bigDecimal.floatValue();
double doubleNum = bigDecimal.doubleValue();
String strNum = bigDecimal.toString();
}
}
10000
10000
10000.123
10000.12345
10000.12345
public class BigDecimalExam {
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("10000.82345");
BigDecimal bigDecimal2 = new BigDecimal("10000.6789");
// 두 수 비교 compareTo 맞으면 0, 틀리면 -1
int compare = bigDecimal1.compareTo(bigDecimal2);
System.out.println(compare);
}
}
bigDecimal1이 크면 1을, 작으면 -1, 같으면 0을 리턴합니다.
1
public class BigDecimalExam {
public static void main(String[] args) {
System.out.println(BigDecimal.ZERO);
System.out.println(BigDecimal.ONE);
System.out.println(BigDecimal.TEN);
}
}
0
1
10
참조 사이트: https://coding-factory.tistory.com/605, https://jsonobject.tistory.com/466, https://velog.io/@new_wisdom/Java-BigDecimal%EA%B3%BC-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-%EC%95%84%EB%A7%88%EC%B0%8C%EC%9D%98-%EB%84%88%EB%93%9C%EC%A7%93