2006년 7월 9일 일요일

C#과 떠다니는 소수점 이야기[2]

이제 제가 하고자 하는 이야기를 할 수 있게 됐습니다.

C#
이 제공하는 float double 부동 소수점 자료형은 IEEE 754 규격을 따릅니다. IEEE 754가 뭔고 하니, 4바이트(32비트) 8바이트(64비트)를 이용하여 부동소수점을 표현하는 방식을 IEEE라는 기관에서 정의한 규격입니다.

이 규격에 따르면 부동소수점은 부호 비트, 지수, 가수로 나누어 표현되며, 4바이트 부동 소수점의 경우 아래의 그림과 같이 부호가 1비트, 지수가 8비트, 가수가 23비트를 사용합니다.









(
그림 1 : 32bit 부동 소수점 자료형의 구조)


부호 비트는 수가 음수인지 양수인지를 구분하기 위해 사용합니다. 0이면 양수, 1이면 음수죠. 지수는 소수점의 위치를 가리키기 위해 사용되고, 가수는 정규화된 값이 담깁니다.


좀 어렵죠? (, 사실 머리에 쏙 들어오는 그런 내용은 아닙니다. 하지만 이 규격을 만든 사람들도 고민을 엄청나게 많이 했습니다. 공부하는 우리는 고생이 덜한 편이라는 것에 위안을 가집시다.)


부호 비트는 명확하니 설명이 더 필요 없을 것 같고, 지수와 가수에 대해 이야기를 좀 해보겠습니다.

우선 가수에 대해 생각해 봅시다. 2진수는 항상 1로 시작합니다. 1001, 11, 1111, 111111 등등 확실히 2진수는 0으로 시작할 수는 없습니다. 10진수나 16진수라면 경우의 수가 많아지지만 2진수는 0이 아니면 1이기 때문에 항상 1로 시작된다는 것을 확신할 수 있습니다.


그래서 가수부의 비트에는 시작하는 1을 제외한 나머지 값만 저장됩니다. 이것으로 23비트만으로 24비트를 담는 효과를 내게 해 주죠. 한편, 가수는 1.xxxxxxx의 값으로 저장됩니다. 예를 들어 11.11 2진수는 가수부에 1.111, 101.01 1.0101로 저장됩니다.
시작하는 1을 제외한 나머지 값만 담기므로 가수부의 비트에 담기는 값은 11.11의 경우는 .111, 101.01의 경우는 .0101만 담기게 됩니다. 이 과정을 일정한 형식으로 바꾸는 정규화(normalization)라고 합니다.


예를 들어 7.25를 정규화하여 가수 비트에 담아봅시다. 7 2진수로 111, 0.25 0.01이니 합치면 111.01이 됩니다. 이를 정규화하면 1.1101이 되죠. 시작되는 1은 제거하고 담는다고 했죠? 그럼 1101만 가수 비트에 담으면 됩니다
.








(
그림 2: 저장된 가수의 모습)


이렇게 해서 가수를 담았습니다. 그런데 소수점의 위치는 상실했습니다. 7.25 111.01(2진수)인데, 1.1101로 저장됐으니 어떻게 이를 복원하죠? 이 문제를 위해 지수가 사용됩니다. (지수가 여자 이름이 아닙니...... 쿨럭;)


지수는 수의 규모를 파악하는 데 유용하게 사용됩니다. 예를 들어 10진수의 경우 72.18 * 102 7218이 되고, 2진수의 경우 1.01 * 22 101이 됩니다. 앞서 잃어버린 소수점의 위치는 이러한 지수의 속성을 이용하여 되찾을 수 있습니다.


한편 지수가 담기는 비트 역시 성능을 위한 고민의 산물입니다. 32비트(4바이트) 부동 소수 자료형의 경우 지수는 126 부터+127 까지의 값을 가질 수 있는데, 2의 보수로 이 값을 표현하면 비교 연산이 엄청 복잡해 집니다(이미 부호가 있는 자료형 안에 또 부호가 들어가 있습니다. 게다가 이 값은 지수라구요.) 연산이 복잡해지면 성능 또한 저하되는 것은 자명하지요.
그래서 지수도 실제 값에 127을 더해 저장하는 바이어싱(biasing)을 사용합니다. 결국 지수부에 담기는 비트에는 0보다 큰 양수만 담깁니다.


조금 전에 7.25의 가수를 비트에 저장했었는데, 이번에는 지수를 담아보겠습니다. 7.25 2진수 111.01 1.1101 * 22로 표현할 수 있으니, 지수는 2입니다. 그런데 실제로 비트에 담을 때는 지수에 127을 더하므로 129를 저장하면 됩니다. 129 2진수로 변환하면 10000001이 되고 이를 지수 비트에 저장하면 다음과 같습니다










(그림 3: 저장된 지수와 가수의 모습)


7.25
의 부호 비트 값은 0이므로 7.25 float 에 담긴 비트의 모습은 최종적으로 다음과 같습니다.










(
그림 4: 7.25 32비트 부동 소수형 비트에 저장된 모습)


제대로 저장됐는지 확인해 볼까요? 저장된 비트 값을 다음 공식을 이용하여 복원해 보면 됩니다.


(-1)부호 * 2(지수 127) * (1+가수
) = 1 0* 2(129 127) * (1+0.1101) = 111.01(2진수) = 7.25(10진수)

이렇게 해서 IEEE 754 규격에 대해 알아봤습니다. 아직 C# 코드는 한 줄도 안 나왔는데 엄청 이야기가 길어졌습니다. 남은 이야기는 다음 포스트에 계속됩니다. ^^

( 이 포스트는 추후 수정될 수 있습니다.)

댓글 1개:

  1. 위에 -127~ 128 이라고 써잇는부분이요

    -128~127 아닌가요? 다른곳하고 보면 좀 다른거 같아서....

    답글삭제