230626 TIL #121 부동소수점 / BigDecimal

김춘복·2023년 6월 26일
0

TIL : Today I Learned

목록 보기
121/543
post-custom-banner

230626 Today I Learned

실수 값 계산에 대해 고민하다가, BigDecimal에 대해 정리를 해봐야겠다는 생각이 들었다.


부동소수점

컴퓨터가 2진수를 이용해 실수를 저장하는 방법 중 하나.

Java에서 일반적으로 실수를 저장하기위해서 float와 double 타입을 쓴다.
두 자료형 모두 부동소수점 방식으로 수를 저장한다.

  • float : 1.4E-45 ~ 3.4028235E38
  • double : 4.9E~324 ~ 1.7976931348623157E308

하지만 부동소수점 방식으로는 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 클래스를 제공한다.

BigDecimal

Java JDK 17 공식문서 : BigDecimal

Java에서 숫자를 오차없이 정밀하게 저장할 수 있는 클래스

  • import java.math.BigDecimal;

  • 10진수를 정확하게 표현할 수 있다.

  • 불변성(immutable)을 가진다. 한번 생성하면 내부 값이 변경되지 않는다.

  • 덧셈, 뺄셈, 곱셈, 나눗셈, 반올림, 비교 등 다양한 연산을 지원한다.

  • 원하는 소수 자릿수를 지정해 숫자를 표현할 수 있다.

  • float가 4바이트, double이 8바이트인 반면 BigDecimal은 기본적으로 16바이트 이상의 메모리를 차지한다.(구현에 따라 더 차지할 수 있다.)

  • 객체 생성과 메모리 사용, 연산의 복잡성 등의 이유로 float, double보다 성능적인 면에서 느리다.

기본 구성

  • intVal : 정수. BigInteger로 정수를 저장한다.
  • precision : 정밀도. 왼쪽부터 0이 아닌 수가 시작하는 위치~오른쪽부터 0이 아닌수로 끝나는 위치 까지 총 자리수
    (0523421.234442100의 precision은 13)
  • scale : 전체 소수점 자리수. 소수점 첫째 자리부터 0이 아닌 수로 끝나는 위치까지의 총 소수점 자리수
    (위 숫자의 scale은 7. 0.00이나 0.0은 1이다.)

초기화 방법

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로 지정할 수 있다.
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 반올림 자리를 명시
profile
Backend Dev / Data Engineer
post-custom-banner

0개의 댓글