본문 바로가기

컴퓨터에서 소수를 표현하는 방법

1. 0.1을 100번 더하면?

 

다음 코드를 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    float num;
    int i;
 
    for (i = 0; i < 100++i)
        num += 0.1;
 
    printf("%f\n", num);
 
    return 0;
}
cs

 

0.1을 100번 더하니 당연히 10.000000이 나올 것 같지만, 실제로 출력되는 것은 10.000002다.

코드나 프로그램이 잘못됐거나 C언어가 이상한 언어이기 때문이 아니라,  부동소수점 방식으로 소수를 표현하기 때문에 발생하는 일이다.

 

 

 

 

2. 부동소수점

 

2진수 소수 1011.0011을 10진수로 바꾸려면 어떻게 해야 될까?

정수와 다를 게 없다. 다음과 같이 바꾸면 된다.

 

 

하지만 컴퓨터 상에서 이를 구현하기는 어렵다.

어떻게 컴퓨터에서 1011.0011을 그대로 표현할 수 있을까?

 

그래서 제안된 것이 부동소수점 방식이다.

부동소수점 방식은 소수를 다음과 같이 4개의 부분으로 나누어 보는 방식이다.

 

 

±: 부호(sign)

m: 가수(mantissa)

n: 기수(base)

e: 지수(exponent)

 

계산기에서 아주 큰 수를 계산하면 4.22E+29와 같은 표현을 볼 수 있는데,

이것이 부동소수점의 대표적인 예다. 

4.22E+29를 풀어쓰면 다음과 같다.

 

 

여기서 +는 부호, 4.22는 가수, 10는 기수, 29는 지수다.

 

기수는 기수법을 정의하는 정수인데, 

기수법이란 2진법 10진법과 같이 수를 나타내는 방법을 말한다.

그러니까 기수는 n진법의 n을 말하는 것이다.

 

위의 예시의 경우 10진수기 때문에 기수가 10이지만, 컴퓨터에서의 기수는 당연히 2다.

 

따라서 실제로 실수를 저장할 때 기수는 (당연히 2기 때문에) 생략하고,

다음과 같이 부호와 가수, 지수만 저장된다.

 

부호(1비트) 지수(8비트)  가수(23비트)

 

부호 (1비트) 지수(11비트)   가수(52비트)

 

비트 수는 다양하게 생각할 수 있지만, 위 그림은 *IEEE의 표준에 따른 것이다.
전자는 단정도 부동 소수점이라 하고, 후자는 배정도 부동 소수점이라 한다.
 
*IEEE(Institute of Electical and Electronics Engineers)
: 미국전기전자협회. 컴퓨터 분야의 다양한 규격을 제정하는 단체.
 
부호는 한 개의 비트로 나타낸다. 양수면 0, 음수면 1.
수의 크기는 가수부와 지수부로 나타낸다. 가수×2^지수의 형태.
이 때 가수부는 정규 표현에 의해 나타내어진다.
'부동(不動)' 소수점이라는 이름이 붙은 이유가 여기에 있다.
 
0.75를 부동 소수점 수로 나타내면, 다음과 같이 여러 형태로 나타낼 수 있다.
 
 
10진수의 경우 소수를 표현할 때 다음과 같은 규칙에 따라야 한다.
 
소수점 이상은 0으로 나타내고 소수점 아래 첫 번째 자리는 0이 아닌 수로 나타낸다.
 
따라서 10진수 0.75는 0.75×10으로만 나타낼 수 있다.
이처럼 어떤 규칙에 따라 (소수점을 고정시켜) 소수를 표현하는 방법을 정규 표현이라 한다.
 
당연한 이야기지만 2진수는 10진수와 정규 표현의 규칙이 다르다.
2진수 정규 표현의 규칙은 다음과 같다.
 
소수점 이상의 값을 1로 고정한다.
 
2진수 1011.0011을 단정도 부동소수점으로 나타낸다고 하면, 
가수부(m)는 다음과 같은 과정에 의해 결정된다.
 
① 소수점 바로 위 첫 번째 자리가 1이 되도록 비트 열을 오른쪽으로 민다.
-> 1.0110011
 
② 소수점 아래를 23비트로 만든다.
-> 1.01100110000000000000000
 
③ 소수점 아래만 떼어낸다.
-> m = 01100110000000000000000
 
 
다음은 지수부다.
 
 
왜  e 가 아닌  e-127 이고, 하필  127 을 빼주는 걸까?
 
앞의 그림을 다시 보자.
 
부호(1비트)  지수(8비트)  가수(23비트) 

 

지수는 8비트다. 따라서 00000000~11111111(255)의 수를 나타낼 수 있다.
그런데 지수부에서는 음수를 표현해야 하는 경우도 생긴다. 
이 때 별도의 비트를 사용하지 않고 나타내기 위해 다음과 같은 방법을 사용한다.
 
① 지수부에서 나타낼 수 있는 최대값을 반으로 나눈다. (여기서는 255÷2=127이 된다.)
② 그 값을 0으로 간주한다.
 
즉, e가 127일 때 0으로 치겠다는 것이다.
따라서 지수에 자리에 e-127이 오는 것이다.
 
이런 방법을 익세스(excess) 표현이라 한다.
 
 
다음은 0.75를 단정도 부동 소수점 수로 나타냈을 때의 비트를 확인하는 예제다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <string.h>
 
#define N 0.75
 
int main()
{
    float num = N;
    unsigned long buff;
    int i;
    char s[34];
 
    memcpy(&buff, &num, 4); //메모리끼리 복사
 
    for (i = sizeof(s) - 1; i >= 0--i)
    {
        if ((i == 1|| (i == 10))
        {
            s[i] = ' ';
        }
        else
        {
            s[i] = (buff % 2+ '0';
            buff /= 2;
        }
    }
 
    s[33= '\0';
 
    printf("%s\n", s);
 
    return 0;
}
cs

 

 

 

 

 

 

3. 오차를 줄이는 방법

 

단정도든 배정도든 컴퓨터가 부동 소수점 방식을 채택하는 한, 

소수 표현에는 오차가 없을 수 없다.

그러나 

 

오차를 예방하는 방법에는 두 가지가 있다.

 

①  실수를 무시한다.

 

무시할 수 있을 만큼 작은 오차는 무시하는 것이다.

예를 들어 10.000002와 10.000000의 차이는 (상황에 따라 다르겠지만) 무시할 수 있다.

0.1g의 화물을 100개 실었을 때의 무게가 정확히 10.000000g일 필요는 없는 것이다.

 

 

② 소수를 정수로 변환한 후 계산

 

적어도 정수 표현에서는 컴퓨터는 정확하다.

그러니까 0.1을 100번 더할 게 아니라 1을 100번 더한 후 그 값을 10으로 나누면 되는 것이다.

 

 

 

 

 

 

 

'Programming > Basis' 카테고리의 다른 글

프로그램이 실행되는 순서  (0) 2019.03.09
메모리  (0) 2019.03.07
비트 연산  (0) 2019.03.04
컴퓨터가 2진수를 사용하는 이유  (0) 2019.03.03
CPU가 수행하는 작업  (0) 2019.02.27