BigDecimal
java
IEEE 754
자바의 float 과 Double 은 값의 정확성을 보장할 수 없다. 그 이유는 자바는 IEEE 754 부동소수점 방식을 사용하기 때문에 정확한 실수를 저장하지 않고 근사치 값을 저장한다.
val a = 100.0000000008
val b = 10.0000000007
assertThat(a - b).isEqualTo(90.0000000001) // Error. Expecting 90.0000000009999
따라서, 금융권 처럼 돈을 다루는 곳에서는 float, double 을 사용한다면 문제가 발생할 가능성이 높다.
따라서, 부동소수점 방식이 아닌 정수를 이용해 실수를 표현하는 BigDecimal
클래스를 사용하면 된다.
Limit of Precision
float 과 double 은 정밀도에 제한이 있다. 그 이유는 부동소수점 표기 방식으로 저장되기 때문인데, 부동소수점이 표현할 수 있는 범위가 제한적이기 때문이다.
실수를 메모리에 표현하는 방법은 가수부, 지수부를 각각 필드에 맞게 할당하면 된다.
저장할 수 있는 공간을 늘려 정밀도를 높이지 않은 이유:
- 한정된 메모리를 최대한 효율적이게 사용하기 위해 자료형 공간을 한정
오차가 없는 계산이 필요한 경우:
- 오차가 없는 계산이 필요할 땐 라이브러리를 사용하거나 소수점 자리수만큼 10을 곱해 정수로 만든 다음 계산하는 방법을 사용
BigDecimal
- BigDecimal 은 변경할 수 없으며 (= immutable 하며) 임의의 정밀도와 부호를 가진 십진수이다.
- BigDecimal 은 불변 객체이기 때문에 연산 결과는 기존의 객체의 값을 변화시키지 않고, 새로운 객체를 반환한다.
BigDecimal 는 다음과 같이 표현이 가능하다:
- unscaledValue × 10^-scale
- unscaledValue : 임의의 유효자리 정수 값
- scale : 소수점 오른쪽의 자릿수를 나타내는 32비트 정수
- e.g BigDecimal 3.14는 unscaledValue 314, scale 2이다.
public class BigDecimal extends Number implements Comparable<BigDecimal> {
private final BigInteger intVal;
private final int scale;
private transient int precision;
private transient String stringCache;
private final transient long intCompact;
private static final int MAX_COMPACT_DIGITS = 18;
...
}
- intVal: 정수로 표현된 BigDecimal 의 값 (= unscaledValue)
- scale: 0 또는 양수인 경우 소수점 오른쪽의 자리 수, 음수일 경우 스케일 되지 않은 숫자 값에 10을 스케일 부정의 거듭 제곱
- precision: unscaledValue 의 자리 수
- stringCache: Used to store the canonical string representation, if computed.
- intCompact: BigDecimal 의 길이("." 포함)가 18자리 이하일 땐, intVal 에 값을 따로 저장하지 않고 intCompact 에 정수 값을 저장
How to use
- 문자열로 숫자를 표현하여 사용
BigDecimal number = new BigDecimal("123.45");
문자열이 아닌 double 타입으로 전달할 경우 이진수의 근사치를 가지게 되어 오차가 발생할 수 있다.
Compare
- Java BigDecimal Zero - Baeldung
- CompareTo: Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method.