java — floating point (부동 소수점)

choi jeong heon
슬기로운 개발생활
3 min readJun 24, 2022

컴퓨터 세계에서의 소수 표현

0.3 을 어떻게 2진수로 표현할 수 있을까?

30 을 2진수로 변환할 때 2를 계속 나눠가면서 2 진수의 각 자리를 확인한다.

소수는 반대로 2를 곱해가며 2 진수의 각 자리를 확인한다.

즉. 0.3(2) = 0.01001100110011001… 이다. ( 1001 무한 반복 )
즉 0.25 + 0.03125 + 0.015625 + … 으로 표현된다.
무한히 반복 되기 때문에 0.3 을 완전하게 표현할 수 없다.

어쩔 수 없이 근사 값을 저장해야 한다.

고정 소수점

정수를 표현하는 bit 수와 소수를 표현하는 bit 수를 정해 놓자.
만약 소수를 표현하는 bit를 2bit, 정수를 표현하는 bit를 6bit 로 지정했다면 0.3 은 000000.01 로 표현 된다.
이는 0.25 와 같기 때문에 정확도가 상당히 떨어진다. 만약 소수 bit를 더 늘릴 경우 더 정밀한 숫자 표현이 가능하지만, 그 만큼 정수 표현 bit가 줄어들기 때문에 큰 수를 표현할 수 없게 된다.

부동 소수점

Oracle Java 설명서에는 다음과 같이 되어 있다.

  • float : single-precision 32-bit IEEE 754 floating point
  • double : double-precision 64-bit IEEE 754 floating point
https://wikidocs.net/81917

부동 소수점 방식에서는 float 의 경우 32 bit 를 위와 같이 나눠 쓴다.

10.3 을 IEEE 754 로 표현해보자.

1010.01001100110011001 … 여기서 소수점을 맨 왼쪽으로 보낸다.

1.010 01001100110011001100 … * 2³ 이것이 정규화된 부동소수점 수 라고 한다.

부호 비트는 양수이므로 0 이고

지수 부분은 3 + 127 = 130 이 된다. 여기서 127을 더하는 이유는 지금은 지수가 3이라 양수이지만 음수가 될 수 있기 때문에 음수 표현을 위해 127을 더한다. 즉, 지수가 -1 이면 127–1 = 126 으로 표현된다.

가수 부분은 소수점 오른 쪽 . 즉 0100100110011001100… 을 23 bit 까지만 표현하면 된다.

꽤나 정밀한 소수 표현이 가능하지만 여전히 오류가 존재한다.

부동 소수점 오류

java 에서 0.3 을 100번 더하면 어떻게 될까?

float a = 0f;
for (int i = 0 ; i < 100 ; i++) {
a += 0.3f;
}
System.out.println(a); // 29.999971

기대했던 30 이 아닌 그 근사치 값을 얻게 된다.

그렇다면 이런 문제는 해결할 수 없는 것일까? 개발자가 감수하고 넘어가야 할 것인가?

Java의 해결책

Java 에서는 정밀한 소수 계산을 위해 BigDecimal 이라는 Class 를 제공한다.
unscaledValue × 10^-scale 로 소수를 관리하는 클래스다.

10.3 의 경우 103 * 10^-1 으로 103 과 1 을 따로 저장해 놓는다.

따라서 정밀한 소수점 계산이 필요한 경우 BigDecimal 클래스를 이용하면 된다.

--

--