(운영체제) 부동 소수점과 이에 따른 문제점(vs 고정 소수점)

2022. 7. 21. 02:27컴퓨터 공학/운영체제

반응형

오늘은 면접에서 질문 받은 것 중에서 기억에 남는 부동 소수점에 관련된 문제에 대해서 얘기해보려 한다.

 

(사실 카테고리가 운영체제가 맞는지도 의문이다.)

 

뭐든지 그 세부사항을 알려면 그 단어의 의미를 먼저 파악해야 한다.

 

'부동 소수점'은 무엇을 말하는 것일까?

 

먼저 '부동'이 뭔지 살펴보자.

 

🧐 안 움직이는 건가요?


한자로는 浮動(Not 動) 이다.

 

그러니까 떠다니며 움직인다는 뜻이다.

(반대어로는 고정 소수점이 있다.)

 

안 움직이는 것이 아니다.

 

 

왜 '뜰 부'를 써서 표현 했을까?(영어로는 float)

 

당연하게도 소수점의 위치를 고정하지 않고 그 위치를 나타내는 수이기 때문이다.

 

 

소수점의 위치를 고정하지 않는다!

 

 

이것은 또 무슨 말인가?

 

 

단어가 이해가 가지 않을때는 반대어를 생각하면 좋다.

 

 

바로 고정 소수점!

 

고정 소수점은 소수점의 위치를 미리 정해놨다.

 

 

어떤 방식으로? 아래와 같은 방식으로.

 

 

🧐고정 소수점


컴퓨터가 32비트라고 가정해보자.

 

그럼 최상위 비트(MST)는 부호를 나타내는 부호 비트가 되고,

 

그리고 다음 15비트는 정수를 나타내는 정수부

 

그리고 남은 16비트는 소수를 나타내는 소수부가 된다.

 

 

예를 들어보자.

 

숫자 127.625가 있다고 생각해보자.

 

127을 2진수로 변환하면 1111111이다.

 

0.625를 2진수로 변환하면 0.101이다.(1/2 + 1/8)

 

 

이를 고정 소수점 방식으로 표현하면

 

최상위 비트는  양수이므로 0이 될 것이고,

 

정수부를 15비트 가지기 때문에127은 000000001111111 이고

 

소수부를 1010000000000000로 가지게 될 것이다.

 

그러니까 최종적으로 0 000000001111111  1010000000000000 이 되는 것이다.

 

그런데 여기서 문제가 생긴다.

 

0.625처럼 나누어 떨어지는 수 말고

 

0.1은 어떻게 표기할 것인가?

 

 

0.1을 2진수로 나타내면

0.0001100110011001100110011001100110011001100110011...

이렇게 나타내진다.

 

이 상황에서 고정 소수점을 사용하면 

 

16비트 이하는 반올림처리되고 사라지게 된다.

 

 

뭐가 문제일까?

 

 

예를 들어 지구에서 지구 밖에 있는 물체에 미사일을 쏜다고 생각하면, 

 

그 정확도가 얼마나 정밀해야 하겠는가!

 

그런데 이 상황에서 고정 소수점을 쓴다면, 16비트 이하의 수는 반영되지 못하는 문제가 생기게 된다.

 

 

그래서 이를 보완하고자 존재하는 것이 바로 부동 소수점이다.

 

 

🧐부동 소수점


부동 소수점은 고정 소수점과는 다르게 정규화라는 것을 거친다. (DB 정규화 아님.)

 

 

부동 소수점에서 정규화는 2진수를 

 

1.xxx * 2 ^n  형태로 변환하는 것을 말한다. 

 

 

근거가 뭘까?

 

이는 과학적 기수법에 따라 변환되고 표기된다.

 

 

바로 다음과 같은 식이다.

 

R = ±m* 10^n

 

이를 해석하면 R = m을 n번 만큼 이동한 것과 같다는 것이다.

 

그리고 이를 2진수에 적용하면 10을 2로 바꿔주기만 하면 된다.

 

 

정규화가 끝마치면 이를 표기해야한다.

 

 

이때 우리는 bias라는 개념을 알아야 한다.

 

 

bias는 n이 음수인 경우를 표기하기 위해서 존재하는 개념이다.

 

 

IEEE 표준에 따르면 32비트에서 bias는 127이라 규정하고 있다.

 

 

그리고 우리는 지수부에 bias + n을 저장하게 된다. 

 

 

n(소수점이 이동한 거리)이 양수라면 127을 초과하게 될 것이고,

 

n이 음수라면 127 미만이 될 것이다.

 

 

이를 통해서 수를 구분하는 것이다.

 

 

예를 들어보자.

 

9.6875라는 수를 2진수로 변환하면 1.0011011 * 2^3이 된다.

 

그럼 n==3이기 때문에 127 + 3의 값인 130의 2진수를 정수부에 저장한다.

 

 

0 10000010 이렇게 말이다.(MST는 양수니까 0)

 

그리고 소수부는 0.0011011을 저장한 후 남는 것은 모두 0으로 채워 저장한다.

 

 

 

이 역시도 근삿값이 저장되는 문제가 발생한다.

 

 

0.1의 경우 정규화를 하면 1.1001001.... * 2^-4가 된다.

 

 

부동 소수점 역시 23비트이후에 나타나는 것을 반올림해서 처리한다.

 

그럼에도 고정 소수점보다 훨씬 많은 수를 표시할 수 있기에 더 정밀하다.

 

그리고 고정 소수점보다 더 큰 수를 다를 수 있다.

 

 

 

🧐발생할 수 있는 문제


이 코드를 실행해보자.

 

#include <iostream>

using namespace std;

int main() {
    float a = 1/10;
    float b = 0.1;
    if (a == b) {
        cout << "Same!\n";
    } else {
        cout << "Different!\n";
    }
    return 0;
}

 

출력되는 것은 뭘까?

 

.

 

.

 

.

 

.

 

.

 

출력 결과는 Different!

 

 

왜 이런 현상이 발생할까?

 

 

바로 그 수에 가장 가까운 값을 저장하기 때문에 그렇다.

 

 

1/10은 0.1인 것처럼 보여도 실제로는 0.1000000000000000055511....이고,

 

0.1은 정확히 0.1인 것처럼 보여도, 0.1의 근삿값인 0.10000000000000001 등의 숫자로 저장되는 것이다.

 

 

 

그럼 동일성을 확인하려면 어떻게 해야할까?

 

 

단순히 0.1과 1/10이 같다는 것을 원한다면 * 10을 하고, int로 형변환을 한 후 비교하면 된다.

 

이렇게 하면 간단하게 비교가 가능하다.

 

 

 

반응형