[回目錄]

一般而言,浮點數沒辦法直接去觀查其 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

 

[回目錄]

edisonx 發表在 痞客邦 PIXNET 留言(0) 人氣()