2006년 7월 6일 목요일

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

"C#의 음수 표현 방식에 대한 이야기"를 읽어본 독자들은 1바이트에 담긴 아름다움을 느끼실 수 있었으리라 생각합니다. (안 읽어봤다면 읽어보세요 클릭!)

이번에는 float과 double과 같은 부동 소수점(Floating Point) 자료형이 어떻게 구성되는가와 이들을 사용할 때 생각해야 되는 부분들에 대해 이야기를 나누고자 합니다.

다른 설명을 시작하기에 앞서 여러분이 한가지 알아둬야 할 것이 있습니다. 바로 10진수를 어떻게 2진수로 표현하는가입니다. 사실 이런 내용을 몰라도 프로그래밍을 잘하는 분들이 많습니다만 공부하는 데 돈이 드는 것도 아니니 조금 시간과 노력을 투자해서 알아둡시다. 게다가 별로 어렵지도 않습니다.

우선 정수를 2진수로 변환하는 방법은 수를 2로 나눠 나머지를 기록하고, 2보다 작은 수가 되어 더 이상 나눌 수 없을 때 계산을 종료하여 기록한 나머지를 밑에서부터 읽어나가는 것입니다.

예를 들어 123을 2진수로 표현하면 다음과 같습니다.

     몫    나머지
123   61    1
61    30    1
30    15    0
15    7     1
7     3     1
3     1     1
1     0     1

123(10진수) = 1111011(2진수)

어렵지 않죠? 이번엔 10진수의 소수를 2진수로 바꾸는 방법에 대해 설명하겠습니다.

소수를 2진수로 바꾸려면 해당 소수에 2를 곱해서 그 결과가 0보다 작으면 0을 기록하고, 1보다 크면 1을 기록한 뒤 결과의 정수 부분을 버리고 소수부분에 다시 2를 곱해 나가는 방식으로 계산하면 됩니다. 예로 10진수의 0.25를 2진수로 바꿔보겠습니다.
    
     결과    0보다큰가?   2진수
0.25  0.5    X           0.0
0.5   1.0    X           0.01 <-- 1.0의 정수 부분 1을 버리면 0만 남으므로 계산끝

또 다른 예를 들어볼까요? 이번에는 0.12를 2진수로 바꿔 보겠습니다.

     결과    1보다큰가?    2진수
0.12  0.24    X           0.0
0.24  0.48    X           0.01
0.48  0.96    X           0.000
0.96  1.92    O           0.0001    <-- 1.92에서 정수 부분 1을 버린다.
0.92  1.84    O           0.00011   <-- 1.84에서 정수 부분 1을 버린다.
0.84  1.68    O           0.000111   <-- 1.68에서 정수 부분 1을 버린다.
0.68  1.36    O           0.0001111  <-- 1.36에서 정수 부분 1을 버린다.
0.36  0.72    X           0.00011110
0.72  1.44    X           0.000111101 <-- 1.44에서 정수 부분 1을 버린다.
....

0.12는 끝이 안나는 군요. ^^; 0.12를 끝까지 계산해보진 않았지만, 만약 결과가 0이 되지 않는다면 끝임없이 2진수 변환을 진행하게 됩니다. 이것은 2진수가 가진 한계 때문입니다. 마치 분수 1/3을 소수로 표현하면 0.33333333333333333333333333333333333333...이 되는 것처럼 말이죠. 다행히도, float는 4byte, double은 8byte안에서 수를 표현해야 하므로 무한대로 계산하지는 않습니다. "부동 소수점 자료형의 "정밀도(Precision)"라는 속성이 어떤 것인지 좀 감이 잡히시죠?

... 다음에 계속 이어집니다. ( 이 포스트는 추후 수정될 수 있습니다.)

댓글 없음:

댓글 쓰기