一般而言,浮點數沒辦法直接去觀查其 16 進位或 2 進位情況,
要觀查 16/2 進位之情況只有一個辦法 : 弄成無號數資料型別去判斷。
重點又變成了:怎麼將浮點數整個記憶體數值,塞到無號數裡面去?
如果 code 長成這樣的話
float f = 1.2f;
unsigned u = (unsigned)f;
那就別天真了,這個動作是做「轉型」,而不是將整個記憶體內容拉進來。
方法其實不只一個,這裡提二個操作方式。
注意的是,基於目前「大多之實作」,
由於 float 是 32 bits,該找的也是 32 bits 之無號數,即 unsigned ;
而 double 是 64 bits,該找的是 64 bits 之無號數,即 unsigned long long。
使用 pointer 操作
先以 float 為例,假設宣告是
float f;
unsigned u;
要將 f 之記憶體內容拉到 u 裡面去,觀念是,
(1) 先將 float 取位址值 ---> &f
(2) 將位址內容轉型成 unsigned* 指標 ----> (unsigned*)&f
(3) 將對 unsigned* 指標做取值(dereference) 動作 -----> *(unsigned*)&f
(4) 再設給 u 值 ----> u = *(unsigned*)&f
(5) 再顯示 u 之 16 進位 ----> printf("%8x\n", u);
完整的程式碼大概就如下所示
float
#include <stdio.h>
int main()
{
float f;
unsigned
u;
printf("Please input a floating number:");
while(scanf("%f",
&f)==1){
u = *(unsigned*)&f;
printf("f=%f , u=%08x\n", f , u);
printf("Please input a floating number:");
}
return
0;
}
double
#include <stdio.h>
int main()
{
double
f;
unsigned
long long u;
printf("Please input a floating number:");
while(scanf("%lf",
&f)==1){
u
= *(unsigned long
long *)&f;
printf("f=%f , u=%016llx\n", f , u);
printf("Please input a floating number:");
}
return
0;
}
使用 union 操作
使用 union 操作常比較沒 pointer 麻煩之問題,也建議新手使用 union 方式操作。
至於速度哪個比較快?不確定,因在 VC / GCC 上測來,我看是不一定的。
float
#include <stdio.h>
union uhex{
float
f;
unsigned
u;
};
int main()
{
union
uhex obj;
printf("Please input a floating number:");
while(scanf("%f",
&obj.f)==1){
printf("f=%f , u=%08x\n", obj.f , obj.u);
printf("Please input a floating number:");
}
return
0;
}
double
#include <stdio.h>
union uhex{
double
f;
unsigned
long long u;
};
int main()
{
union
uhex obj;
printf("Please input a floating number:");
while(scanf("%lf",
&obj.f)==1){
printf("f=%lf , u=%016llx\n", obj.f , obj.u);
printf("Please input a floating number:");
}
return
0;
}
runtime error R6002
這問題不是每次都會發生。
若手邊 compiler 是 vc,有時會出現以下錯誤訊息
runtime error R6002
- floating point support not loaded
原因出在,使用 floating 變數時,沒給初始值就用 scanf,
導致出錯,將上述的 float f , double f 給予初值即可避開這問題,
不知這算不算是微軟的 bug。
不支援 %016llx ?
在做顯示 double 16 進位時,
注意到上面 printf 有個格式可能會有問題:%016llx,這是 C99 以後才有支援的東西,
未必所有 compiler 對於 printf 都有支援,此時可用另一種方式顯示,以 pointer 為例
pointer
#include <stdio.h>
int main()
{
double
f;
unsigned
a[2];
printf("Please input a floating number:");
while(scanf("%lf",
&f)==1){
*(double*)a=f;
printf("f=%f , u=%08x%08x\n", f , a[1], a[0]);
printf("Please input a floating number:");
}
return
0;
}
union
#include <stdio.h>
union uhex{
double
f;
unsigned
u[2];
};
int main()
{
union
uhex obj;
printf("Please input a floating number:");
while(scanf("%lf",
&obj.f)==1){
printf("f=%lf , u=%08x%08x\n", obj.f , obj.u[1], obj.u[0]);
printf("Please input a floating number:");
}
return
0;
}
方法也不只一種,這裡提供的只是其中一個,對 bitwise 操作較熟也可那麼做。
唯應注意,上述的 array,是先顯示 [1] ,再顯示 [0],
原因是假設電腦是屬於 little endia,
若為 big endian ,則應先顯示 [0],再顯示 [1]。
顯示二進位
現在,我們可以將 float / double 其記憶體內容,dump 至 unsigned / unsigned long long ,
顯示其 16 進位,那顯示二進位呢?
很遺憾的是,這部份沒辦法直接用 printf 已有之格式引數輸出,
因 printf 並不會檢查 data type size,所以當初也沒有設計 %b 之類的東西,
這部份必需自己寫,但並不難。
事實上,上面的前半段:將 float / double 記憶體內容 dump 到無號數裡還是必需的,
接下來再對該無號數做 2 進位的輸出。
下述都以 pointer 方式將浮點數塞到無號數裡,再以 2 進位方式顯示該無號數,
便為,以2進位方式顯示浮點數,這裡先以 float 為例
#include <stdio.h>
int main()
{
float
f=0.0f;
unsigned
u;
unsigned
mask;
printf("Please input a floating: ");
while(scanf("%f",
&f)==1){
mask=0x80000000U;
u
= *(unsigned*)&f;
while(mask){
printf("%d", (u&mask)!=0U);
mask>>=1;
}
printf("\nPlease input a floating: ");
}
return
0;
}
而 double 也是類似做法。
包成副函式
由於上述所提供之函式,在觀查浮點數進制時實為常用,
建議全都寫成副函式,以便日後方便呼叫。
至於副函式規劃部份,這裡不再提供意見,只給一份 code 當參考。
原始碼
/*******************************************************************/
/*
*/
/* filename : DisplayBit.c */
/* author : edison.shih/edisonx */
/* compiler : Visual C++ 2008 */
/* date : 2011.03.07 */
/*
*/
/* A.L.L. R.I.G.H.T.S. R.E.S.E.R.V.E. */
/* */
/*******************************************************************/
#include <stdio.h>
//--------------------------------------------------
void bin32(void* v); // 2 進位方式顯示32 位元資料
void bin64(void* v); // 2 進位方式顯示64 位元資料
void hex32(void* v); //16 進位方式顯示32 位元資料
void hex64(void* v); //16 進位方式顯示64 位元資料
//--------------------------------------------------
int main()
{
float f = 2.34f;
double
d = -1.234;
printf("\nf=%e\n", f);
hex32((void*)&f), bin32((void*)&f);
printf("\nd=%e\n", d);
hex64((void*)&d), bin64((void*)&d);
return
0;
}
////////////////////////////////////////////////////////////////////
//--------------------------------------------------
//2 進位方式顯示32 位元資料
void bin32(void* v)
{
unsigned
mask=0x80000000U;
unsigned
value = *(unsigned*)v;
while(mask){
printf("%d", (mask&value)!=0U);
mask>>=1;
}
puts("");
}
//--------------------------------------------------
//2 進位方式顯示64 位元資料
void bin64(void* v)
{
unsigned
long long mask=0x8000000000000000ULL;
unsigned
long long value = *(unsigned
long long*)v;
while(mask){
printf("%d", (mask&value)!=0ULL);
mask>>=1;
}
puts("");
}
//--------------------------------------------------
//16 進位方式顯示32 位元資料
void hex32(void* v)
{
printf("%08x\n", *(unsigned*)v);
}
//--------------------------------------------------
//16 進位方式顯示64 位元資料
void hex64(void* v)
{
unsigned
a[2];
*(unsigned
long long*)a=*(unsigned long long*)v;
printf("%08x%08x\n", a[1],
a[0]);
}
執行結果
f=2.340000e+000
4015c28f
01000000000101011100001010001111
d=-1.234000e+000
bff3be76c8b43958
1011111111110011101111100111011011001000101101000011100101011000
留言列表