Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
Today
Total
관리 메뉴

빵굽는 개발자

부동 소수점 연산 오차와 BigDecimal 본문

프로그래밍/Java

부동 소수점 연산 오차와 BigDecimal

Ha-hyun 2023. 1. 19. 14:19

부동 소수점

자바에서는 부동 소수점을 표기하기 위한 자료형이 2가지 존재하는데 각 자료형의 정보는 아래와 같다.

자료형 범위 비트 바이트
float
1.4E-45 ~ 3.4E38 (1.4×10^-45 ~ 3.4×10^38)
32 4
double
4.9E-324 ~ 1.8E308 (4.9×10^-324 ~ 1.8×10^308)
64 8

 

부동 소수점의 연산 오차 ?

아래의 코드는 부동 소수점 연산을 했지만 정확한 값이 아닌 오차가 발생하고 있는데 그렇다면 왜? 이런 문제가 발생하는지 얘기하자면 컴퓨터에서는 정확한 실수 값의 표현이 불가능하기 때문에  연산 과정에서 오차가 발생하고 쌓여서 최종적으로는 아래 코드와 같은 오차가 발생하게 된다.

 

자바 개발자라면 한번 쯤은 들어봤거나 읽었을 책 이펙티브 자바의 ITEM 60에는 정확한 답이 필요하다면 float과 double을 피하라고 적혔있는데 그렇기 때문에 특히 결재나 금융같이 정확한 부동 소수점 연산이 필요한 곳에서는 float, double을 피해야 한다. 그렇다면 자바에서는 해당 자료형을 쓰지 않는다면 정확한 연산을 위해서 어떤 방식을 사용하는걸까?

double d = 1.03 - 0.42 // // 0.6100000000000001

 

BigDecimal

BigDecimal은 자바에서 숫자를 정밀하게 저장하고 표현할 수 있는 유일한 방법인데 자바 언어에서 돈이나 소수점을 표현하기 위해서는 꼭 사용해야한다. 그렇다고 BigDecimal이 장점만 존재하는 것은 아니며 느린 속도와 사용하기 불편한 연산 방법이다.

 

상수

아래 코드와 같이 자주 쓰는 상수 값은 쓰기 편하기 미리 지정되어 있어서 사용할 수 있다.

BigDecimal.ZERO // 0

BigDeciaml.ONE // 2

BigDeciaml.TEN // 100

 

초기화

BigDecimal을 선언해서 사용할 때 주의점이 존재하는데 부동 소수점 타입을 바로 전달하게 되면 넣어준 값과는 다른 값을 가지게 된다. 그렇기 때문에 부동 소수점 타입을 문자열로 변경 후 바꿔주는 과정이 필요하다.

new BigDecimal(0.01); // 0.01000000000000000020816681711721685132943093776702880859375

new BigDecimal("0.01"); // 0.01

BigDecimal.valueOf(0.01); // 0.01

 

동등 연산

BigDecimal을 연산하기 위해서는 주의할 점이 있는데 원시값이 아닌 오브젝트이기 때문에 동등 비교인 equals, compareTo 같은 연산자로 비교하면 문자열과 같다고 생각하고 비교를 해야한다.

BigDecimal a = new BigDecimal("1.11");
BigDecimal b = new BigDecimal("1.110");

// 레퍼런스 주소에 대한 비교 연산자로 사용하면 다른 객체로 오브젝트로 인식해 false를 반환
a == b; // false

// equals 사용 시 값이 완전히 동일해야 true를 반환
a.equals(b); // false

// compareTo 사용 시 숫자로 비교하기 때문에 0을 반환, 0은 동등
a.compareTo(b); // 0

 

사칙 연산

사칙연산의 경우 코드가 길어지고 간결해 보이지 않게 변한다.

BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("2");

// 덧셈
a.add(b); // 12

// 뺄셈
a.subtract(b); // 8

// 곱셈
a.multiply(b); // 20

// 나눗셈
a.divide(b); // 5

 

그 외 함수 및 연산 옵션

옵션 설명
abs() 절대값
min() 두 수 중 최소값
max() 두 수 중 최대값
reminder() 나누기 후 나머지 값 연산
setScale() 소수점 처리
stripTrailingZeros() 소수점 오른쪽 0 제거
RoundingMode.FLOOR 소수점 절사
RoundingMode.CEILING 소수점 올림 ( 음수에서는 절사 )
RoundingMode.HALF_EVEN 소수점 반올림
MathContext.DECIMAL32 전체 자리수 7 제한
MathContext.DECIMAL64 전체 자리수 16 제한
MathContext.DECIMAL128 전체 자리수 34 제한
MathContext.UNLIMITED 전체 자리수를 허용