컴퓨터가 실수를 표현하는 방법은 "고정 소수점 표현", "부동 소수점 표현" 이렇게 2가지가 있습니다. 이에 대해서 살펴보도록 하겠습니다.
고정 소수점 표현(Fixed-Point Representation)
고정 소수점 표현은 말 그대로 소수점의 위치를 고정시켜 표현한다는 의미입니다. 예를 들어 16비트를 사용하는 경우, 앞의 8비트는 정수 부분과 부호 부분을 표현하고, 나머지 8비트는 소수 부분을 표현합니다.
즉, 소수점의 위치를 고정시켜서 정수를 표현하는 부분과 소수를 표현하는 부분을 나눈다는 것입니다.
Ex)
5.3410=101.010101112이때, 정수부의 경우 뒷자리부터 채우며, 소수부의 경우 앞자리부터 채웁니다.
그리고 남는 부분은 모두 0으로 채웁니다.
=> 00000101.01010111
고정 소수점 표현은 숫자 표현이 간단하기 때문에 구현하기가 편하고 연산 속도가 빠르지만 표현 가능한 수가 적고 제한된 부분(절댓값이 아주 큰 숫자이거나, 소수점 아래가 아주 길어서 정확한 실수값을 사용하지 않는 경우)에서만 사용합니다. 그래서 컴퓨터에서는 이 방식을 잘 사용하지 않습니다.
부동 소수점 표현(Floating-Point Representation)
컴퓨터 내에서 정수는 "오버플로우" 상황이 아닌 이상, 정확한 값이 보장됩니다. 그러나 실수의 경우에는 무한 개의 숫자를 표현하기 위해서 가능하면 '정밀하게 표현'해야 합니다. 그러한 상황일 때는 '고정 소수점 표현 방식'은 적절하지 않습니다.
부동 소수점 표현은 소수점의 위치를 고정시키지 않고 가수와 지수를 사용해 실수를 표현한다는 의미입니다.
여기서 가수는 유효숫자를 나타내며, 지수는 소수점의 위치를 나타내고, 부호는 가수의 부호를 나타냅니다.
10진수 $0.34_{10}$을 2진수로 바꾸면 $0.010101110_2$이 되는데 이를 부동소수점 표현으로 나타내려면
먼저 "정규형"으로 바꿔야 한다. 정규형은 정수 부분에 "0이 아닌 수를 하나만 남기는 것"입니다.
즉, 정규형은 2진수를 1.xxxxxxx $\times 2^n$로 나타내는 것을 말합니다.
$0.010101110_2$ 을 정규형으로 표현하면 $1.0101110 \times 2^{-2}$이 되는데,
이제 정규형 표현에서 지수와 가수를 나누면 됩니다
8비트에서는 부호 1비트, 지수 3비트, 가수 4비트를 할당합니다.
16비트의 경우 부호 1비트 지수 5비트, 가수 10비트로 구성됩니다.
32비트의 경우 부호 1비트 지수 8비트, 가수 23비트로 구성됩니다.
64비트의 경우 부호 1비트 지수 11비트, 가수 52비트로 구성됩니다.
8비트로 할당하기 때문에 지수에는 3비트를 할당합니다.
이때, 음수 형태의 지수를 표현하기 위해 "초과표현"을 사용합니다.
초과 표현(Excess Representation)
초과 표현 중 주로 사용하는 '3 초과 표현'은 "표현하고자 하는 수보다 3을 초과시켜서 표현하는 것"입니다.
숫자 | 3초과 표현 | 2의 보수 표현 |
-4 | 100 | |
-3 | 000 | 101 |
-2 | 001 | 110 |
-1 | 010 | 111 |
0 | 011 | 000 |
1 | 100 | 001 |
2 | 101 | 010 |
3 | 110 | 011 |
4 | 111 | 100 |
복잡해보이는 초과 표현을 사용하는 이유는 바로 "대소판정을 쉽게 할 수 있기" 때문입니다.
"정규형"에서는 지수를 맞추는 과정이 필요가 없으며, 가수를 볼 필요 없이 지수가 크기만 하면 그 수가 더 큰 수가 됩니다. (지수가 같다면 가수를 비교해야 합니다.) 결론적으로, 지수 영역을 위해 초과 표현을 사용하면 같은 부호일 경우에는 전체의 비트 패턴을 정수처럼 생각해도 대소 관계를 파악할 수 있습니다.
초과표현을 일반화하면 $2^{n-1} - 1$ (n : 지수 bit) 방식으로 나타낼 수 있습니다.
그렇게 컴퓨터 내에서 진행되는 부동 소수점 표현 과정을 보면 다음과 같습니다.
$1.0101110 \times 2^{-2}$에서 가수의 부호는 +이기에 부호할당에 0을 적습니다.
$1.0101110 \times 2^{-2}$ 에서 지수에 3비트를 할당하기에 3초과표현을 사용합니다.
이때 지수가 -2이기 때문에 3을 초과시켜 지수 부호할당에 1을 적습니다. (정확히는 001)
그리고 가수에서 4비트만 사용하기에 앞에서 4비트를 잘라 1010을 적습니다.
결론적으로 $0.34_{10}$는 컴퓨터 내부에서 $0 \, 001 \, 1010 \rightarrow 00011010_{2}$로 저장됩니다.
히든 비트(Hidden Bit)
정규형을 사용하면 반드시 정수 부분은 1이 되게 되는데, 이 1을 없애면 소수점의 한 자리를 더 표현할 수 있게 되는데 이렇게 없애버린 1을 "히든 비트(Hidden Bit)"라고 합니다. 히든 비트를 사용하면 가수의 정수 부분을 표현하지는 않더라도 있다고 생각하고 진행합니다. 그렇게 되면 가수 부분을 한 비트를 더 표현할 수 있기에 조금 더 정확한 숫자를 표현할 수 있게 됩니다.
8비트를 이용해 만들 수 있는 가장 작은 실수는 $0 \ 000 \ 0000_{(2)}$입니다. 그러나 이 숫자는 히든 비트를 사용하기에 $0_(10)$이 아닌 $0.125_{(10)}$입니다. 그렇기에 특별한 값(Special Value)으로 $0 \ 000 \ 0000_{(2)}$를 $0_{(10)}$으로 "정의"합니다.
언더플로우(Underflow)
8비트를 사용하는 경우 표현할 수 있는 숫자의 범위는 다음과 같습니다.
가장 작은 절대값의 양의 실수 | $0 \ 000 \ 0001_{(2)} = +0.1328125_{(10)} $ |
가장 작은 절대값의 음의 실수 | $1 \ 000 \ 0001_{(2)} = -0.1328125_{(10)} $ |
가장 큰 절대값의 양의 실수 | $0 \ 111 \ 1111_{(2)} = +31.0_{(10)} $ |
가장 큰 절대값의 양의 실수 | $1 \ 111 \ 1111_{(2)} = -31.0_{(10)} $ |
이때, 가장 작은 절대값의 양의 실수와 가장 작은 절대값의 음의 실수 사이의 범위 부분과, 가장 큰 절대값의 양의 실수 이상 부분과 가장 큰 절대값의 음의 실수 부분을 표현할 수 없습니다. (그 외에도 어떤 수를 0으로 나누었을 때 발생하는 무한대 값, 허수 등의 NaN을 표현하기 위한 값, 언더플로우가 최대한 발생하지 않게 하기 위한 denormalized form 등의 경우를 위한 값 등 특수한 경우들이 더 있습니다.)
가장 작은 절대값의 양의 실수와 가장 작은 절대값의 음의 실수 사이의 범위 부분을 표현하지 못하는 것을 "언더플로우(Underflow)",
가장 큰 절대값의 양의 실수 이상 부분과 가장 큰 절대값의 음의 실수 부분을 표현할 수 없는 것을 "오버플로우(Overflow)"라고 합니다.