부동소수점 오차 이유 - budongsosujeom ocha iyu

부동 소수점에 대해서 알아봅시다. 컴퓨터 구조 시간에 상세하게 배울 텐데요. 그렇기 때문에, 깊게 하지는 않겠습니다. 여기에서는 왜 부동 소수점 연산에서 오차가 발생하는지 정도만 볼 거에요. 왜 0.1이 부동 소수점 표현방식으로는 오차가 생길 수 밖에 없는지 알기만 해도 충분할 거 같네요.

부동소수점 오차 이유 - budongsosujeom ocha iyu

moo를 공용체로 선언했어요. 공용체의 특징은 변수들이 같은 메모리 공간을 공유한다는 것인데요. v와 r은 같은 데를 공유합니다. 이걸 그림으로 그려보면 아래와 같이 됩니다.

부동소수점 오차 이유 - budongsosujeom ocha iyu

moo형인 I를 선언하면 v도 쓸 수 있어요. 그리고, r도 쓸 수 있는데 공통된 공간을 공유합니다.

부동소수점 오차 이유 - budongsosujeom ocha iyu

그렇기 때문에 I.v에 3.14를 저장하고, I.r을 읽어버리는 것도 가능해요. 어짜피 같은 메모리 공간을 공유하기 때문에, 실수형을 저장해 놓고 정수형으로 읽어버려서, 비트값만 검사하면 끝입니다. 실제로, 실수 값이 메모리에 어떻게 들어가는지, 비트별로 보기 위해서 공용체를 썼어요.

118.625를 32비트 단정도로 표현해 봅시다. 일단 118과 0.625를 따로 따로 계산해 보는 게 낫겠네요. 0.625는 5/8입니다. 이것은 (4+1)/8로 표현할 수 있기 때문에, 1/2 + 1/8로 표현할 수 있어요. 그리고, 118을 2진수로 변환하면 1 0001 1000이에요. 118.625를 일단 2진수로 바꾸면 100011000.101이 됩니다. 118.625는 이렇게 변환이 가능합니다. 문제. 이걸 어떻게 손으로 변환하실 건가요? 118은 변환했다고 치고요. 0.625만 변환하면 됩니다. 이것 역시 진법 시스템을 이용하면 쉽게 바꿀 수 있어요. 10진수를 2진수로 바꾸듯이 똑같이 바꾸면 됩니다.

따라서 118.625는 다음과 같이 표시가 됩니다. 1110110.101 이제 이것을 정규화 시켜 볼 겁니다. 이게 무슨 어려운 소리냐고 이야기 하실 수 있을 듯 싶은데요. 그렇게 어렵진 않으니까 천천히 읽어보세요. 우리는 어떠한 실수 k를 b*2^u꼴로 표현을 할 건데요. 이 때 1<=b<2를 만족하게 할 거에요. 뭔가 많이 본 것 같은데. 1<=b<2라면, b는 이런 식으로 표현이 되어야 할 거에요. b는 2진수로 1.xxxxx와 같이 표현이 되어야 할 거에요.

그런가요? 그러면 가수부는 .110110101이 되고, 지수부는 6이 되는 것이지요. 32비트 IEEE 754 형식에서 bias는 127입니다. 따라서 제가 표시한 6이라는 숫자에 127을 더한 게 지수부에 들어갑니다.

부동소수점 오차 이유 - budongsosujeom ocha iyu

다음 예제인 0.0703125를 봅시다. 조금 더 어려운데요. 똑같이 해 봅시다. 일단 정수부는 0이니까 그대로 냅두고, 소수부분만 2진수로 변환을 먼저 해 봅시다.

멘붕이네요. 어찌 되었던 잘 변환하면 .0001001이 나옵니다. b*2^u꼴로 변환을 해야 하는데, 이 때 b는 1이상 2미만인 실수가 되어야 합니다. 그럴려면 이렇게 되어야 겠군요.

32비트 단정밀도에서는 bias가 127입니다. 따라서, 지수부에는 -4+127인 123이 들어가야 할 겁니다. 그리고 실수부에는 00100...이 들어갈 거에요. 123을 2진수로 표현하면 0111 1011입니다.

부동소수점 오차 이유 - budongsosujeom ocha iyu

이것을 토대로 종합해 보면 최상위 비트는 0이 나올 겁니다. 양수이기 때문입니다. 그리고 지수부에 0111 1011이 들어가고, 가수부에 00100... 이 들어갈 거에요. 종합해 보면 0.0703125는 요렇게 저장이 되겠네요.

부동소수점 오차 이유 - budongsosujeom ocha iyu

공학용 계산기가 없으면 정말 힘들겠군요. 사실 분모를 2^23으로 해 놓고 x값을 구하는 트릭을 쓰면 손으로 계산할 정도는 될 지도 모르겠어요.

0.1은 어떻게 될까요? 0.1 = 1/10인데 이건 u/2^k꼴로 표현이 가능할까요? k가 엄청나게 큰 수라고 해 봅시다. u는 자연수, 혹은 0이라고 합시다.

표현이 불가능 합니다. 1번째 줄에서 u는 자연수, 혹은 0이라고 가정했는데, 5는 2로 떨어지지 않기 때문에, 분모에 5가 남게 됩니다. 따라서, u는 정수가 되지 못합니다. 따라서, 0.1을 표현할 수 없습니다. 이는 3진수로 0.1로 표현이 가능한 1/3이, 10진수 체계에서는 무한 소수가 되는 이유입니다. 이것도 똑같이 해 봅시다.

k가 매우 커져도, u는 결코 정수가 될 수 없습니다. 분자가 0이 아닌 정수이고, 분모가 2와 5가 아닌, 다른 소인수를 가지면 무한 소수가 되는 이유입니다. 그렇기 때문에 0.1은 2진법으로 정확히 표현할 수 없습니다. 따라서, 어느 정도 오차가 발생할 수 밖에 없어요. 일반화를 시켜봅시다.

bj/bm을 부동 소수점 방식으로 표현한다고 해 봅시다. u는 그냥 정수라고 해 봅시다. 이 때, bj와 bm의 gcd는 1입니다. 식을 잘 정리해 보면, 2^k가 bm으로 떨어지지 않으면 표현이 불가능하다는 것을 알 수 있어요. 2^k가 bm으로 떨어지는 조건은 bm이 2^t꼴로 표현이 되어야 한다는 것입니다.

그렇지 않다면, bm이 2 이외의 다른 소인수를 가지고 있다는 것입니다. 그러면 2^k/bm은 정수가 되지 못합니다. 13/33 역시 마찬가지입니다. float나 double형으로 완전히 표현할 수 없어요. 0.1을 float로 표현했습니다. 이 때, 비트에는 아래와 같이 저장이 되어 있습니다.

부동소수점 오차 이유 - budongsosujeom ocha iyu

이것은 실제로 어떤 값을 표현하고 있을까요? 이걸 10진수로 변환을 해야 겠는데요. 일단, 1.xxxx에서 xxxx에 해당하는 것 먼저 읽어봅시다.

부동소수점 오차 이유 - budongsosujeom ocha iyu

실제로는 b값이 1.60000002384185791015625겠네요. 이제 지수부를 읽어볼까요? 지수부는 123을 표현하고 있어요. bias 값이 127이라고 했어요. 실제로는, 지수값이 -4라는 겁니다. 1.60000002384185791015625에 2^-4를 곱해야 겠네요. 즉, 16으로 나눠야 겠네요. 그러면 어떻게 될까요?

부동소수점 오차 이유 - budongsosujeom ocha iyu

실제값인 0.1하고 차이가 있어요. 0.1은 u/2^k꼴로 (단 u는 정수) 표현을 할 수 없기 때문에 그래요. 결론적으로. 그러한 점 때문에, 부동 소수점 표현 방식은, 오차가 생길 수 밖에 없어요. double은 float보다, 지수부를 표현하는 비트도 많고, 가수부를 표현하는 비트 또한 많습니다. 하지만, 이러한 오차에서 자유로울 수 없다는 것은 동일합니다.

나중에 더 자세히 이야기 할 기회가 있을 듯 싶습니다. 어떻게 더하고, 곱하고 빼는지. 그리고 교환 법칙과 결합 법칙 같은 것이 성립하는지. 이러한 것까지 설명하려면 상당히 길어질 거 같습니다. 오늘은 그냥. 아. 부동 소수점 방식이 이러한 한계를 가지고 있구나 정도만 알고 넘어가면 좋을 듯 싶습니다