Programming/Java

Java - BigDecimal 사용하는 이유 (feat.부동소수점의 부정확성)

Jan92 2022. 8. 18. 00:47
반응형

BigDecimal 사용하는 이유

Java 기반 프로그램에서 소수점에 대한 정확한 계산이 필요하거나 화폐 단위를 다룰 때는 BigDecimal을 사용하는데요.

자바에서 숫자를 표현하는 데이터 타입에 대해서 간단하게 살펴보고, 이어서 BigDecimal을 사용해야 하는 이유는 무엇인지 살펴보겠습니다.

 

 


 

Java에서 숫자를 표현하기 위한 데이터 타입

자바에서 숫자를 표현하기 위해 사용하는 데이터 타입은 크게 '정수형 데이터 타입''실수형 데이터 타입'으로 나눠집니다.

 

 

- 정수형 데이터 타입(Integer Types)

데이터 타입 (자료형) 크기 저장 가능한 값의 범휘
byte 1byte (8bit) -128 ~ +127
short 2byte (16bit) -32,768 ~ + 32,767
int 4byte (32bit) -2,147,483,648 ~ +2,147,483,647 (약 -21억 ~ +21억)
long 8byte (64bit) -9,223,372,036,854,775,808 ~ +9,223,372,036,854,775,807 (약 -900경 ~ +900경)

 

- 실수형 데이터 타입 혹은 부동소수점 타입(Floating-Point Types)

데이터 타입 (자료형) 크기 저장 가능한 값의 범위
float 4byte (32bit) 1.40239846E-45f ~ 3.40282347E+38f
double 8byte (64bit) 4.94065645841246544E-324 ~ 1.79769313486231570E+308

float은 소수점 이하 6자리, double은 소수점 이하 15자리의 정밀도를 가지고 있으며, double은 소수점을 가지는 수 중에서 float 보다 더 큰 소수점을 가지는 숫자를 담을 때 적합합니다.

 

(E notation은 exponent 즉, 지수 표기입니다. 4.9E-324는 4.9*10^-324를 의미합니다.)

 

 

 

BigDecimal을 사용하는 이유

double val = 0.0;
for (int i=0; i<10; i++) {
    val += 0.1;
}
System.out.println("val : " + val);    // val : 0.9999999999999999
System.out.println("0.1 + 0.2 == 0.3 ? " + (0.1 + 0.2 == 0.3));    // 0.1 + 0.2 == 0.3 ? false

위 예시를 통해 알 수 있는 것처럼 실수형 타입(부동소수점 타입)인 float, double은 정확한 표기가 필요한 외화 환율이나 화폐를 다룰 때는 적절하지 않은데요. 이러한 결과가 나타나는 이유는 '부동소수점의 부정확성' 때문입니다.

 

java에서 float과 double 타입은 애초에 과학, 공학 계산을 위해 설계되었으며, 넓은 범위의 수에 대한 근사치를 빨리 산출하기 위한 이진 부동소수점 연산을 수행합니다.

 

부동소수점의 부정확성에 대한 원인

컴퓨터에서 부동소수점을 표현하기 위해 사용하는 형식은 여러 가지가 있지만, 현재 거의 대부분이 호환성을 위해 미국 전기전자공학회(IEEE)에서 표준화한 IEEE 754 형식을 사용하고 있는데요.

부동소수점의 부정확성에 대한 원인은 위 이미지의 예시와 같이 IEEE 754 표준에 의해 정수, 소수 등과 같은 숫자가 2진으로 저장되기 때문에 나타납니다.

(십진수 0.1은 이진수로 바뀌면서 무한 소수가 되고, 컴퓨터는 가장 근사한 값을 반환하는데 여기에서 부정확성이 나타나는 것입니다.)

 

=> 따라서 소수점에 대한 정확한 계산이 요구되거나 화폐 단위를 다룰 때는 BigDecimal을 사용해야 하는 것입니다.

 

 

 

BigDecimal의 특징 (간단하게)

BigDecimal은 원시 타입이 아니기 때문에 연산자를 사용할 수 없으며 add, multiply, divide 등의 메서드를 사용하여 연산해야 합니다.

또한 불변성(Immutable)을 가지고 있는데, 때문에 연산 메서드를 사용하면 객체 내부의 값이 변경되는 것이 아니라 연산 결괏값을 갖는 새로운 객체가 생성되어 반환됩니다.

 

때문에 원시 타입의 연산에 비해 속도가 느리다는 단점이 있지만, 정확도를 위해서는 무조건 BigDecimal을 사용해야 합니다.

 

 

 

< 참고 자료 >

 

컴퓨터의 데이터 표현 2 실수 표현

2진수의 소수점 표현의 한계 고정 소수점 부동 소수점 1. 2진수의 소수점 표현의 한계 십진수 0.25를 이진수로 바꾸는 방식은 아래와 같다. 이 방법을 일반화 한 절차다. 절대값이 1보다 작은 10진

acknowledge.tistory.com

 

 

자바 변수, 프리미티브 타입, 리터럴

변수 선언, 원시 데이터 타입, 리터럴

velog.io

반응형