written by yechoi

(번역) 고정소수점 표현에 대한 이해 본문

Born 2 Code/C, C++

(번역) 고정소수점 표현에 대한 이해

yechoi 2020. 11. 1. 17:29
반응형

👉이 글은 Berkeley의 Introduction to Fixed Point Number Representation를 번역했습니다. 다소 의역한 부분이 있으며, 오역한 부분이 있다면 댓글을 부탁드립니다.

 

Introduction to Fixed Point Number Representation

In real life, we deal with real numbers -- numbers with fractional part. Most modern computer have native (hardware) support for floating point numbers. However, the use of floating point is not necessarily the only way to represent fractional numbers. Thi

inst.eecs.berkeley.edu

 

 

개론: 현실 속 실수들

현실에서 우리는 많은 실수를 다룹니다 - 분수를 포함한 숫자들요. 현대 컴퓨터의 대부분은 부동소수점 실수에 대한 (하드웨어적인) 지원을 포함하고 있습니다. 그러나 부동소수점 실수는 분수를 표현하기 위한 유일한 방법은 아닙니다. 이번 글에선 실수를 표현하는 고정소수점에 대해 설명할 겁니다. 고정소수점 사용은 정확도보다는 성능이 중요한 DSP(Digital Signal Processing)이나 게임 어플리케이션에서 자주 쓰입니다. 나중에 보겠지만 고정소수점 계산은 부동소수점 계산보다 빠릅니다.

 

 

실수로 시작하죠

이러한 이진법 표현의 수가:

110101

다음과 같은 값을 표현한다는 것을 떠올려보세요.:

1 * 25 + 1 * 24 + 0 * 23 + 1 * 22 + 0* 21 + 1 * 20

= 32 + 16 + 4 + 1

= 5310

이제 53을 2로 나누면, 결과는 26.5일겁니다. 그러나 우리가 정수 표현만 할 수 있다면, 이걸 어떻게 표현해야 할까요?

 

 

 

 

바이너리 포인트

위의 26.5와 같이 분수를 표현하는 방법의 핵심은 바이너리 포인트(binary point)의 개념입니다. 바이너리 포인트는 10진법에서 10진법 소수점 과 같습니다. 숫자의 정수 부분과 분수 부분을 나누는 구분자 역할을 하죠.

십진법에서 십진법 소수점은 계수가 10^0 = 1 로 곱해져야 한다는 것을 암시하죠. 예를 들어 26.5라는 수에서 계수 6은 10^0 =1의 가중치가 적용됩니다. 소수점 옆의 4에는 어떤 일이 일어나나요? 우리의 경험으로 미루어 봤을 때, 10^-1의 가중치가 있겠네요. 우리는 "26.5"가 "이십 육 그리고 반"의 가치를 지닌다는 것을 알아요. 왜냐하면 이렇게 표현되니까요.

2 * 101 + 6 * 100 + 5 * 10-1 = 26.5

이진법 표현에도 십진법 소수점과 같은 개념이 적용될 수 있습니다. "바이너리 포인트"를 만들면요. 10진법에서처럼 바이너리 포인트는 계수가 2^0 =1 이라고 할 수 있어요. 바이너리 포인트 왼쪽의 모든 밑기수(digits)(또는 비트)는 2^0, 2^1, 2^2 와 같은 가중치를 지니죠. 바이너리 포인트 오른쪽의 밑기수(또는 비트)는 2^-1, 2^-2, 2^-3 등의 가중치를 갖습니다. 예를 들면 다음과 같은 숫자는

11010.1

이러한 값을 표현합니다.:

2^5 2^4 2^3 2^2 2^1 2^0 2^-1 2^-2 2^-3
... 1 1 0 1 0 1 0 ...

= 1 * 24 + 1 * 23 + 0 * 22 + 1 * 21 + 0* 20 + 1 * 2-1

= 16 + 8 + 2 + 0.5

= 26.5

 

 

시프팅이 키입니다

주의를 기울인 독자라면 53과 26.5의 비트 패턴이 정확히 같다는 것을 알 수 있을 겁니다. 유일한 차이점은 바이너리 포인트의 위치입니다. 10진법 53에는 바이너리 포인트가 없습니다. 다르게 말하면 바이너리 포인트가 가장 오른쪽, 0의 위치에 있다고 할 수 있죠. (십진법에서 53과 53.0이 같은 걸 떠올려 보세요.)

2^5 2^4 2^3 2^2 2^1 2^0 Binary Point 2^-1 2^-2 2^-3
1 1 0 1 0 1 . 0 0 0

10진법수 26.0는 바이너리 포인트가 10진법수 53보다 한 칸 왼쪽에 있습니다:

2^5 2^4 2^3 2^2 2^1 2^0 Binary Point 2^-1 2^-2 2^-3
0 1 1 0 1 0 . 1 0 0

지난 수업을 복습하면, 정수를 오른쪽으로 1비트 밀면 2로 나누는 것과 같다고 얘기를 했었죠. 이러한 시프팅을 정수 나누기로 했다고 치면, 정수에선 분수 부분이 없으므로 바이너리 포인트 오른쪽 부분을 표현할 수 없었습니다. 그러나 이건 바이너리 수를 표현하는 데의 정수의 한계일 뿐입니다.

수학적으로 보통 고정 바이너리 포인트가 주어지면, 오른쪽으로 1비트 옮긴다는 건 항상 2로 나누는 것과 같습니다. 비슷하게 왼쪽으로 1비트 밀면 2로 곱하는 것이겠죠.

 

 

고정소수점 표현

위의 시프팅 과정은 고정소수점 표현을 이해하는 데 핵심입니다. 실수를 컴퓨터(또는 일반적인 모든 하드웨어)로 표현하려면, 우리는 바이너리 포인트를 숫자 어딘가에 고정하므로써 고정소수점을 정의할 수 있습니다. 그리고 우리는 수를 표현할 떄 이러한 절대적인 관습을 따를 겁니다.

개념적으로 고정소수점을 정의하기 위해서, 우리는 두가지 변수가 필요합니다:

  • 표현하고자 하는 수의 가로 너비
  • 숫자 상의 바이너리 포인트의 위치

앞으로는 fixed<w,b> 표현법을 사용할 건데요. w는 전체적으로 사용된 비트의 수(수의 너비)이고 b 는 가장 영향이 작은 비트로 부터 바이너리 포인트의 위치를 의미합니다.

예를 들어서 fixed<8,3> 은 8비트로 고정된 수이고요, 오른쪽 세 개가 분수 파트 입니다. 이런 비트 패턴인거죠:

0 0 0 1 0 1 1 0

아래와 같은 실수를 표현합니다:

00010.110

= 1 * 2^1 + 1 * 2^-1 + 1 * 2^-2

= 2 + 0.5 + 0.25

= 2.75

컴퓨터에선 하나의 비트 패턴이 다른 걸 표현할 수 있다는 걸 기억하세요. 같은 비트 패턴이더라도 fixed<8,5> 처럼 다른 타입을 캐스팅한다면 다음과 같이 표현될 겁니다:

000.10110

= 1 * 2^-1 + 1 * 2^-3 + 1 * 2^-4

= 0.5 + 0.125 + 0.0625

= 0.6875

이 비트 패턴이 정수를 표현한다면 이렇죠:

10110

= 1 * 2^4 + 1 * 2^2 + 1 * 2^1

= 16 + 4 + 2

= 22

 

 

음수

지금까지 우리는 양수에 대해 이야기를 했는데요. 음수도 표현하고 싶겠죠? 그때는 고정소수점을 어디에 둬야 할까요?

컴퓨터에서 우리는 음수를 표현하기 위해 2의 보수를 활용했습니다. 2의 보수 숫자의 특징 중 하나는 양수나 음수의 계산이 같다는 것이죠. 계산에는 더하기, 빼기 뿐만 아니라 놀랍진 않겠지만 시프팅도 포함됩니다. 양수에서 하는 것처럼 사인 2의 보수를 사인 익스텐션(번역자 주: 부호 비트를 고순위의 레지스터에 복사하는 것으로 이 확장은 보통 1 또는 2의 보수 2진수로 수행)과 함께 1비트를 오른쪽으로 옮겨서 2로 나눌 수 있어요.

이 글의 시작에서 고정소수점 실수가 정수를 시프팅 했던 것(바이너리 포인트를 0이 아닌 곳에 위치하게 한 것)이라 논의했던 것을 떠올려 보세요. 2의 보수 음수에도 시프트 연산이 작용한다는 것을 연결하면, 음수가 고정소수점에서 어떻게 표현되는 지 깨달을 수 있어요. 바로 2의 보수를 사용하는 거죠.

아래의 수는 2의 보수를 4비트로 표현한 겁니다:

Bit Pattern Number
Represented (n)
n / 2
1 1 1 1 -1 -0.5
1 1 1 0 -2 -1
1 1 0 1 -3 -1.5
1 1 0 0 -4 -2
1 0 1 1 -5 -2.5
1 0 1 0 -6 -3
1 0 0 1 -7 -3.5
1 0 0 0 -8 -4
0 1 1 1 7 3.5
0 1 1 0 6 3
0 1 0 1 5 2.5
0 1 0 0 4 2
0 0 1 1 3 1.5
0 0 1 0 2 1
0 0 0 1 1 0.5
0 0 0 0 0 0

이 표를 보면 -2.5를 비트 패턴을 1011을 표현할 수 있다는 것을 알게 된다. 바이너리 포인트가 1에 있다는 것을 가정만 한다면 말이다.

 

 

고정소수점 실수 표현의 장단점

이쯤이면 고정소수점 실수가 정수 표현에 가깝다는 걸 알았을 겁니다. 정수와 고정소수점 실수는 바이너리 포인트의 위치에서만 차이가 나요. 사실 정수 표현 자체를 고정소수점 실수 중 바이너리 포인트가 0에 위치한 '특별한 케이스'라고 생각할 수도 있겠네요. 정수에 활용할 수 있는 컴퓨터의 모든 수학 연산은 고정소수점에도 적용할 수 있습니다.

따라서 고정소수점 연산의 장점은 정수 연산이 컴퓨터에서 그렇듯 직관적이고 효율적인 것입니다. 우리는 정수 연산을 위해 설계된 하드웨어를 고정소수점 연산을 하는데 재사용할 수 있어요. 다른 말로 하자면 고정소수점 연산은 컴퓨터에겐 '공짜'인 셈이죠.

고정소수점 실수의 단점은 부동소수점 표현과 비교했을 때 표현할 수 있는 범위가 작고 정밀도도 떨어진다는 점입니다. 예를 들어 fixed<8,1> 표현에서 우리의 분수 파트는 0.5만큼만 정확할 수 있어요. 0.75 같은 수는 표현할 수 없죠. fixed<8, 2>로 표현할 수 있기는 한데, 정수부를 잃게 되겠죠.

 

 

C에서 고정소수점 실수 사용하기

C는 고정소수점 "타입"을 가지고 있진 않습니다. 그러나 고정소수점 표현의 본래 특징상, 필요하지도 않아요. 고정소수점 실수 연산이 정수와 같다는 것을 떠올려보면, 곡정소수점 연산에 타입 int 를 그대로 재사용할 수 있습니다. 화면에 표현할 때나 다른 "타입"과 연한살 때 (예를 들면 fixed<32.7>int를 더할 때) 바이너리 포인트의 위치만 고려해주며 됩니다.

 

 

결론

고정소수점은 단순하지만 컴퓨터에서 분수를 표현할 때 아주 강력합니다. 컴퓨터의 정수 연산 서킷을 모두 재사용하므로써, 고정소수점은 부동소수점 연산보다 훨씬 빠릅니다. 게임이나 DSP 어플리케이션에서 많이 사용되는 이유죠. 반대로 부동소수점 표현이 제공하는 범위와 정밀도는 따라갈 수 없습니다. 무엇을 택할지는 프로그래머나 서킷 디자이너로서 당신이 선택해야겠죠.

반응형