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의 표준에 따른 것이다.
소수점 이상은 0으로 나타내고 소수점 아래 첫 번째 자리는 0이 아닌 수로 나타낸다.
소수점 이상의 값을 1로 고정한다.
① 소수점 바로 위 첫 번째 자리가 1이 되도록 비트 열을 오른쪽으로 민다.-> 1.0110011
② 소수점 아래를 23비트로 만든다.-> 1.01100110000000000000000
③ 소수점 아래만 떼어낸다.-> m = 01100110000000000000000
부호(1비트) | 지수(8비트) | 가수(23비트) |
지수는 8비트다. 따라서 00000000~11111111(255)의 수를 나타낼 수 있다.
① 지수부에서 나타낼 수 있는 최대값을 반으로 나눈다. (여기서는 255÷2=127이 된다.)② 그 값을 0으로 간주한다.
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 |