[CS] 부동소수점 오차

지상 최악의 개발자·2024년 1월 24일

컴퓨터님은 실수를 정확히 연산하지 못해

0.1 + 0.1은 당연히 0.2다. 컴퓨터, 너도 그렇게 생각하지?

0.1 + 0.1 == 0.2
>> false

컴퓨터에서 실수의 연산 결과는 우리의 생각과 다를 수 있다. 이런 현상은 특정한 프로그래밍 언어에서만 나타는게 아니라 모든 컴퓨터에서 전반적으로 나타나는 문제다. 문제의 원인이 컴퓨터가 수를 2진수로 다루는 것에 있기 때문이다.

정수의 표현법

모두가 알다시피 컴퓨터는 모든 수를 2진수로 변환해 저장한다. 0와 1만 다룰 줄 아니까.
컴퓨터는 10진수 숫자 7을 00000111로 변환해서 다루는 것이다.

컴퓨터 내부의 메모리, 디스크는 0 또는 1의 두가지 상태중 하나를 저장할 수 있는 방을 수억개씩 가지고 있다고 생각하면 된다. 위의 경우 8칸의 방을 할당해서 00000111을 저장할 것이다.

실수의 표현법

오케이, 여기까지 문제 없다. 그러면 실수는 어떻게 저장할까?

1. 고정소수점 fixed point

(부호 1비트)(정수부 15비트)(소수부 16비트) //총 32비트

오케이, 10진수 실수를 2진수로 변환한 다음에 그냥 정수부랑 소수부 두개로 나눠서 따로 저장하면 되겠네! 단순명료하다.
그런데 문제가 있다. 표현할 수 있는 범위가 너무 작다.

2. 부동소수점 floationg point

그래서 등장했다. 실수를 가수부와 지수부로 나누는 방법이다.

±(1.가수부)×2^(지수부-127)
(부호 1비트)(지수부 8비트)(가수부 23비트) //총 32비트. IEEE float

똑똑한 사람들이 고민한 결과 이 수식을 이용해서 컴퓨터에 실수를 저장하기로 합의했다. 이제 자릿수의 제한이 있는 고정소수점 방식보다 더 넓은 범위의 실수를 저장할 수 있게 되었다. 현재 대부분의 컴퓨터는 부동소수점 방식을 따른다.

그런데 문제가 있다. 부동소수점 방식도 여전히 실수를 2진수로 변환하기 때문에 여기서 문제가 발생한다.
0.125 같은 숫자는 2진수로 0.001로 깔끔하게 떨어진다.
하지만 0.1 같은 경우 2진수로 표현하면 0.0001100110011001100110011001100110011001100110011...가 무한히 반복되는 순환소수가 된다!!! 이걸 컴퓨터에 저장하려면 당연히 일정부분부터는 버릴 수 밖에 없다. 바로 여기서 오차가 발생한다.

float a = 0.1; //이건 0.1에 아주 가까울 뿐인 다른 숫자다!

해결 못하나요?

몇가지 방법이 있다. 아주 정확히 계산해야 할 필요가 있을때 쓰는 방법이다.

1. 정수로 저장하자

1.23 같은 실수는 100을 곱해 123으로 저장하면 된다.

2. 반올림 문법을 사용하자

반올림 문법을 적절하게 사용해서 문제를 막으면 된다.

3. double 자료형을 쓰자

float말고 double을 써보자. double은 64비트를 할당해주는 자료형이다. 당연히 오차가 훨씬 더 작아진다.

0개의 댓글