아래글 원문의 주소는
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 로 변환한 값 형태로 넘겨줘야 하는 것 같다
'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글
C++ 캐스팅 dynamic_cast, reinterpret_cast, static_cast (0) | 2012.11.01 |
---|---|
부동소수점과 다른 형과의 변환을 사용하는 이유 (0) | 2012.11.01 |
멤버함수포인터와 std::map 의 응용 (0) | 2012.11.01 |
UINT_PTR (32비트 및 64비트 호환 포인터연산) (0) | 2012.11.01 |
행렬 배열레퍼런스 비트연산으로 가져오기 아이디어 (0) | 2012.11.01 |