반응형

아래글 원문의 주소는

http://cafe.naver.com/cplc/83117

이지만 글을 조금 수정합니다, 추가한 붉은 글씨가 수정한 부분입니다

 

 

32bit 부동소숫점 연산식을 알아야 합니다.

단순히 데이터 자릿수가 실수로 변환되어지는 것은 아니고요.. 실제론 꽤 복잡한 연산식으로 만들어진 것입니다.

 

32비트의 각 자릿수 마다 정해진 규정이 있고요 풀어보자면,,,,,

맨 끝 비트(32번째 비트)가 부호를 결정짓고요 간단히 부호(s)라고 정의합니다.

그다음 24번째 비트부터 31번째 비트까지의 8비트(1byte)가 지수입니다. 이를 e라고 정해두고,

그다음 맨처음 비트부터 23번째 비트까지가 가수입니다.  이를 m이라 정한다면

 

  부동소숫점 = (-1)^s * 2^(e-127) *1.m

 

 

지수부(2^(e-127))는 첫번째 비트부터 2의 승수를 곱해주는데 즉,2^0,2^1, 2^2, 2^3, 2^4, 2^5, 2^6, 2^7의 값을 첫번째 비트부터 순서대로 곱하도록 하고

 그냥 지수부를 비트열로 표현하면 2^e 로 표현되지만 현재는 DWORD 에서 float 로 변환하는 과정임으로 (2^(e-127)) 와 같은 식이 사용된다

 이렇게 변환하는 이유는  float 의 표현 범위를 늘리기 위함

 

가수부(1.m)는 마지막 비트(23번째=>이미지 숫자상으론 22) 부터 2의 마이너스 승수를 곱해야 합니다.

 

                   즉, 2^-1, 2^-2, 2^-3, 2^-4 .......... 2^-23의 값을 마지막 비트부터 순서대로 곱해야 합니다.

                    => 2^-2 = 1/(2^2) 즉 소수점 부분을 표현하기 위한 수치

 

 

 

 

32bit의 데이터값이 16#4015c28f 이란 것을 기준해서 풀어보도록 해볼까요?

먼저 이 데이터를 2진수로 변환해 봐야 해당된 비트의 상태를 알 수 있습니다.

변환해 보면..... 휘리릭...!!~~

 

(16진수)16#4015c28f  => (2진수) 2#0100 0000 0001 0101 1100 0010 1000 1111 로 되는 것을 알 수 있습니다..

앞의 16#, 2# 가 진수 표현을 나타냄

 

어떻게 변환됬냐구요?

그냥 16진수의 1자리를 4자리의 2진수로 바꾸면 됩니다.

 

    첫번째 비트는 2^0   ---  1

    두번째 비트는 2^1   ---  2

    세번째 비트는 2^2   ---  4

    네번째 비트는 2^3   ---  8

                    => 2^n ( 0<=n <= 8 )

 

  - 0000 = 16#0

  - 0001 = 16#1

  - 0010 = 16#2

  - 0011 = 16#3

  - 0100 = 16#4

  - 0101 = 16#5         : 대충 눈치 채셨죠?

  - 0110 = 16#6

  - 0111 = 16#7

  - 1000 = 16#8

  - 1001 = 16#9          : 아직 모르시겠나요?

  - 1010 = 10 = 16#a   : 16진수에선 0 ~ f 까지랍니다.

  - 1011 = 11 = 16#b

  - 1100 = 12 = 16#c   : 이젠 아셨기를...

  - 1101 = 13 = 16#d

  - 1110 = 14 = 16#e

  - 1111 = 15 = 16#f    : 다 해버렸네...

 

그럼 본론으로 들어가서...

정리해서 보면

 

(2진수) 2#0100 0000 0001 0101 1100 0010 1000 1111

 

[float 로 변환]

 

  [부호] = 2#0 = 0  

  [지수] = 2#0100 0000 0 = 1000 0000

              2#0 에서2# 다음에 오는 0 은 이미 부호비트로 취급했음으로 다음인 1 을 포함한 8(지수부)의 비트열을 가져옴 

            = (1*2^7) + (0*2^6) + (0*2^5) + (0*2^4) + (0*2^3) + (0*2^2) + (0*2^1) + (0*2^0)

            = (1*128) + (0*64) + (0*32) + (0*16) + (0*8) + (0*4) + (0*2) + (0*1) = 128+0+0+0+0+0+0+0 = 128

 

  [가수] = 001 0101 1100 0010 1000 1111 = (0*2^-1) + (0*2^-2) + (1*2^-3) + (0*2^-4) + (1*2^-5) + (0*2^-6) + (1*2^-7) +

              (1*2^-8) + (1*2^-9) + (0*2^-10) + (0*2^-11) + (0*2^-12) + (0*2^-13) + (1*2^-14) + (0*2^-15) + (1*2^-16) +

              (0*2^-17) + (0*2^-18) + (0*2^-19) + (1*2^-20) + (0*2^-21) + (0*2^-22) + (0*2^-23)

            = (0*0.5) + (0*0.25) + (1*0.125) + (0*0.625) + (1*0.3125) + (0*0.015625) + (1*0.0078125) + (1*0.00390625) +

              (1*0.001953125) + (0*0.0009765625) + (0*0.00048828125) + (0*0.000244140625) + (0*0.0001220703125) +

              (1*0.00006103515625) + (0*0.000030517578125) + (1*0.0000152587890625) + (0*0.00000762939453125) +

              (0*0.000003814697265625) + (0*0.0000019073486328125) + (1*0.00000095367431640625) +

              (1*0.000000476837158203125) + (1*0.000000238418579101562) + (1*0.000000119209289550781)

            = 0.169999957084656

 

결과를 계산식에 대입해 보면

    (-1)^0 * 1.169999957084656 * 2^(128-127)

  = 1 * 1.169999957084656 * 2

  = 2.33999991416931

  ≒ 2.34 

 

정리하느라 2시간 걸렸습니다.  -_-;;;

직접 계산하려고 하시지 말고 GM-WIN에서 [REAL to DWORD] 와 [DWORD to REAL] 펑션을 이용해서

시뮬레이션 해보시는게 건강에 이로울 듯 하네요...

 


[float 로 변환과 그 역]

 

 이러한 연산 가수부분의 변환 연산 과정이 있음으로 float 에서 DWORD 로 변환 할대는 가수부를 정수부형태 즉

16#...... 의 형태로 변환 해야 하기 때문에

float 값을 (DWORD) 로 그냥 캐스팅하면 정수부분만 나오게 되고 float 전체 값을 캐스팅 하기 위해선

 

float a;

 

*((DWORD*)&a)

 

의 방법으로 캐스팅하면 float 값을 DWORD 로 변환한 값을 얻을 수 있게 된다

 


[더하는 말로..]

 

directx 에서

float PointSize = 10.0f;
   _pD3dDevice->SetRenderState( D3DRS_POINTSIZE , *((DWORD*)&PointSize));

 

의 함수중 *((DWORD*)&PointSize 이것을 이렇게 변환 하는 이유를 추측해 보자면 이 함수안에서 사용 되는 값이

DWORD 값을 float 로 변환해 쓰이기 때문에 이전에 float 값을 DWORD 로 변환한 값 형태로 넘겨줘야 하는 것 같다

 

 

반응형

+ Recent posts