부동소수점 과 소수점 오차 이해하기

nyoung·2024년 5월 23일
0

0.1 + 0.2 가 0.3이 되지 않는 이유를 명확하게 설명하기.

컴퓨터의 정수와 소수 계산

컴퓨터는 2진법을 사용한다. 즉, 0과 1로 이루어져있다. 이것은 누구나 다 아는 사실이다.

2진법으로 된 컴퓨터는 숫자를 어떻게 표현할까?

먼저, 정수 부를 표현해보자. 아래는 소수 부가 없는 정수 부를 표현할 수 있는 java의 자료형이다.

  • short (2byte)
    2^15 ~ 2^15
  • int (4byte)
    2^31 ~ 2^31 -1
  • long(8byte)

이렇게 byte 수에 따라 표현할 수 있는 숫자의 크기가 달라진다.

정수의 범위가 넘어가면 더 큰 bit수가 필요하다.

그렇다면 소수 계산은 어떻게 할까?

소수 계산

먼저 소수를 계산할 때는 고정 소수점 방식과 부동 소수점 방식이 있다.

우리가 흔히 소수를 생각했을 때, 정수(소수점 위)와 소수(소수점 아래) 부로 나눈다.

ex) 23415.34 일때, 정수 부: 23415 소수 부: 34

이것은 고정 소수점 방식의 생각과 비슷하다.

고정 소수점 방식

고정 소수점 방식(Fixed Point)은 bit를 특정 비율로 나눠, 정수, 소수 부분에 할당한다.
말 그대로, 소수점을 고정시킨다.

예를 들어, 4byte를 2byte 씩 고정시켜보자.

맨 앞의 부호 비트를 빼면, 31bit가 남으므로, 정수 부를 15bit, 소수 부를 16bit로 나누자.

그러면 정수 부는 0 ~ 2^14까지 표현이 가능하다.
소수 부는 2^-1 ~ 2^-16까지 표현이 가능하다.

이렇게 되면 직관적으로 메모리에 실수 표현을 할 수 있지만, 소수점이 고정되어 있기 때문에 표현 가능한 범위가 매우 작아진다.

Java의 float(4byte) 기준으로 생각해 봤을 때 정수 부 소수 부 반으로 나누면, Maximum이 2^15 - 1이기 때문에, 32767이상의 정수 부를 가진 소수를 표현하지 못하게 된다.

또한 정수 부가 큰 소수나, 소수 부가 큰 소수가 있어 서로 낭비하는 메모리가 생기기도 한다.

그러면 표현하고자 하는 숫자에 따라 소수점을 고정시키지 않고 그때그때 변경하면 되지 않을까?

컴퓨터는 이러한 생각으로, 낭비하는 메모리를 줄이기 위해 부동소수점 방식으로 표현하게 되었다.

부동 소수점 방식

부동 소수점 방식(Floating Point)은 IEEE 754 표준에 따라 지수와 가수 부로 나눈다.

Floating Point라는 말과 같이, 소수점을 고정하지 않고 왔다갔다 할 수 있도록 한다.

부동 소수점 방식은 소수점을 1.xxx * 2^N 으로 보는 방식이다.

아래와 같이 1비트의 부호 비트, 8비트의 지수부, 23비트의 가수 부로 나눠진다.

여기에서 xxx 부분이 가수 부, N 부분이 지수 부이다.

이해를 위해 잠깐 10진수로 생각해보자.

254.15431일때 2.5415431 * 10^2 이런식으로 표현하는 것과 같다.

부동 소수점 방식은 실수의 값 자체를 가수부에 집어넣기 때문에 큰 비트를 표현할 수 있다.
10진수 숫자 8자리를 표현할 수 있는 칸이 있다고 생각해보자. 부호는 고려되지 않는다.

그리고 25.15431를 표현해보자.

고정 소수점 방식은 4자리씩 나눈다. 정수 부, 소수 부 4자리 씩 밖에 없는 곳에서 10진수 25.15431를 표현할 수 없다. 소수 부가 5자리이기 때문이다.

하지만 부동 소수점은 2.515431 * 10^2이기 때문에, 8자리를 사용해서 표현할 수 있다.
2515431 (7자리), 2 (1자리)

이런 식으로 부동 소수점 방식을 사용하면 더 많은 수를 표현할 수 있다.

무한 소수와 근삿값

무한 소수란, 소수점 아래의 0이 아닌 숫자가 무한히 많은 소수이다.
부동 소수점은 실수를 이진수로 표현하는데, 일부 소수는 이진수로 정확히 표현할 수 없기 때문이다.
1/3일 때 0.333333333.... 이 되는 것과 같다. 특정 값은 2로 나누어 떨어지지 않는다.
이렇게 되면, 결국 무한대 자릿수를 저장할 수 없으니, 결국 메모리 한계까지 소수점을 집어넣고, 나머지는 반올림한다.

그렇다면 정확한 계산이 필요한 로직에서 이런 근삿값 문제를 어떻게 해결할 수 있을까?

부정확한 소수 문제를 해결하는 방법

첫 번째로, 소수 부분을 정수 부분으로 변경해 계산할 수 있다.
자바스크립트에서는 BigInt 를 사용하면 큰 소수를 큰 정수로 변환해 계산할 수 있다.

둘째로는, 외부 라이브러리를 사용하는 것이다. js에서는 BigDecimal 라이브러리가 대표적이다.

profile
코드는 죄가 없다,,

0개의 댓글

관련 채용 정보