본문 바로가기

[Kotlin&Spring] 5기 내일배움캠프

[Kotlin&Spring] 5기 Java 부동소수점방식과 BigDecimal 타입

컴퓨터는 10진수를 2진수로 표현해서 저장한다

그렇다면 실수의 경우는 어떻게 저장할까?

Java 의 primitive 타입인 float와 double 의 저장 방식을 살펴보았다 

 

float 자료형의 경우는 32bit 의 크기를 갖는다

이때 비트를 3개의 부분으로 나누어 수를 저장한다

1.

맨 앞 1비트는 부호(flag) 정보를 저장한다

ex) 음수는 1, 양수는 0으로 저장한다

2.

10진수의 수를 2진수 수로 변환한다

ex) 5.125(10) -> 101.001(2)

1의 자리만 정수로 남기고 나머지 수를 소숫점 뒤로 보낸다

ex) 1.01001 * 2^2

mantissa 부분을 맨 뒷자리 23자리에 넣는다

ex) 01001: mantissa

이 부분이 무한하게 이어질 경우 23자리 이후의 값은 제거한다

제거하는 값 때문에 보통 오차가 발생한다

3.

지수부분 + 127(2^7) 의 값을 2진법으로 변환하고 부호 뒤의 8자리에 넣는다

ex) 2 + 127 = 129 -> 10000001

 

완성된 결과는 5.125 -> 010000000101001(2) 가 된다

 

double 타입의 경우는 위의 float 타입과 유사한 형식을 64bit 에 적용한다고 한다

Java는 위의  IEEE 754 부동소수점 방식을 primitive 타입에 채택했다

 

java.Math.BigDecimal 은 Java 언어에서 사용되는 고정소수점 및 임의정밀도 소수점 방식을 제공하는 클래스이다

위의 2번에서 발생할 수 있는 정밀도 손실 문제를 방지한다

또한 계산 속도는 double , float을 사용하는 경우보다 조금 느리지만 정밀한 결과를 보장한다.

 

BigDecimal의 주요 필드는 다음과 같다

1. intVal: BigInteger 타입(최소 70바이트, 타입의 한계가 없다)에 정수값을 저장한다

2. scale: 전체 소수점 자리수로, 32bit의 크기를 갖는다

소수점 첫째 자리부터 오른쪽부터 0이 아닌수로 끝나는 위치까지의 총 소수점 자리수를 말한다

ex) 012345.67890 scale: 4

      예외) 0.00, 0.0 scale: 1

3. precision: 정밀도의 의미로, 숫자를 구성하는 전체 자리수를 말한다

왼쪽부터 0이 아닌 수가 시작하는 위치부터 오른쪽부터 0이 아닌 수로 끝나는 위치까지의 총 자리수를 말한다

ex) 012345.67890 precision: 9

 

BigDecimal 은 위 필드를 사용하여 아래와같이 수를 표현한다 

intValue * 10^(-scale)

 

BigDecimal 은 primitive 타입이 아니기 때문에 객체를 생성해서 값을 초기화한다

BigDecimal value = new BigDecimal("12345.6789");

 

double 타입의 값이 아닌 String 으로 인자를 전달해야 부동소수점 방식으로 저장되지 않는다

또한 객체를 생성하는 방식이기 떄문에 ==(참조값 주소 비교) 이 아닌 equals() 메소드(값만 비교)로 값을 비교한다

 

BigDecimal 는 임의로 소수점을 처리할 수 있는 기능을 제공한다

나누기 메서드인 divide() 인자로 나눌 수, scale(소숫점 몇째자리까지 표현할 것인지)과 올림등의 타입 Enum 값을 받는다

RoundingMode 라는 enum 으로 올림/내림/반올림 등의 상태를 관리한다

ex) a.divide(b, 3, RoundingMode.HALF_EVEN);

 

나머지메서드인 remainder() 인자로 나눌 수, precision(전체자릿수) 를 받는다

precision java.math.MathContext 클래스의 상수로 관리한다

ex) a.remainder(b, MathContext.DECIMAL128);

 

RDB를 관리하는 언어 중 하나인 MySQL에도 BigDecimal과 같은 고정소수점 타입을 DECIMAL 이라는 이름으로 제공한다

컬럼 선언 방법은 다음과 같다

DECIMAL(precision(전체자리수), scale(소수점 이하 자리수))

precision 설정없이 선언시 기본값 10.0을 적용한다

ex) foo DECIMAL(5, 2) DEFAULT 0.00 NOT NULL

지정된 소수 자리수보다 많은 자리의 값을 저장할 경우 지정된 소수 자리수 이하는 floor 처리된다

 

정밀한 수의 연산과 처리에 있어서, 또한 오버플로우나 언더플로우를 예방하기 위해 BigDecimal 의 사용은 도움이 될 것 같다

그러나 간단한 수의 연산과 저장에는 빠르게 실행되고 저장되는 primitive 타입을 채택하는 것이 맞을 것 같다

개발자의 판단이 중요하다고 여겨진다

 

차근차근 노력하면 뭐든지 잘 할 수 있을 것 같다

오늘도 화이팅해서 남은 하루 잘 마무리하자 ~

 

출처: https://youtu.be/-GsrYvZoAdA?si=fLGXQ6Hx7gj7qb1z