2016-11-28 91 views
1

背景:
我在玩位級編碼(這不是家庭作業 - 只是好奇)。我在網上和一本名爲Hacker's Delight的書中發現了很多很好的材料,但是我遇到了一個在線問題。C位浮點轉換意外輸出

它要求將整數轉換爲浮點數。我用下面的鏈接作爲參考通過對問題的工作:

How to manually (bitwise) perform (float)x?
How to convert an unsigned int to a float?
http://locklessinc.com/articles/i2f/

問題和問題:
我想我理解的過程不夠好(我想記錄下在評論過程中),但是當我測試它時,我不理解輸出。

測試用例:
float_i2f(2)返回1073741824
float_i2f(3)返回1077936128

我希望看到類似2.0000和3.0000。

我把這個轉換搞亂了嗎?我想也許這是一個內存地址,所以我想也許我錯過了訪問實際編號所需的轉換步驟?或者,也許我打印不正確?我打印我的輸出是這樣的:

printf("Float_i2f (%d): ", 3); 
printf("%u", float_i2f(3)); 
printf("\n"); 

但我認爲,在C印花方法是罰款,無符號值(我已經習慣了編程的Java)。

感謝您的任何建議。

代碼:

/* 
    * float_i2f - Return bit-level equivalent of expression (float) x 
    * Result is returned as unsigned int, but 
    * it is to be interpreted as the bit-level representation of a 
    * single-precision floating point values. 
    * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while 
    * Max ops: 30 
    * Rating: 4 
    */ 
    unsigned float_i2f(int x) { 
     if (x == 0){ 
      return 0; 
     } 

     //save the sign bit for later and get the asolute value of x 
     //the absolute value is needed to shift bits to put them 
     //into the appropriate position for the float 
     unsigned int signBit = 0; 
     unsigned int absVal = (unsigned int)x; 

     if (x < 0){ 
      signBit = 0x80000000; 
      absVal = (unsigned int)-x; 
     } 

     //Calculate the exponent 
     // Shift the input left until the high order bit is set to form the mantissa. 
     // Form the floating exponent by subtracting the number of shifts from 158. 
     unsigned int exponent = 158; //158 possibly because of place in byte range 

     while ((absVal & 0x80000000) == 0){//this checks for 0 or 1. when it reaches 1, the loop breaks 
      exponent--; 
      absVal <<= 1; 
     } 

     //find the mantissa (bit shift to the right) 
     unsigned int mantissa = absVal >> 8; 

     //place the exponent bits in the right place 
     exponent = exponent << 23; 

     //get the mantissa 
     mantissa = mantissa & 0x7fffff; 

     //return the reconstructed float 
     return signBit | exponent | mantissa; 
    } 
+2

你應該使用'%f'格式說明符告訴'printf'將該值解釋爲浮點值。通過使用'%u',你要求它打印一個無符號整數。但是,這可能是由於嚴格別名而導致的未定義行爲,以及將可變參數傳遞給函數的方式。你可能更好地創建一個'float'變量,並使用'memcpy'將結果中的整數位直接複製到float中。字節順序仍然是一個問題。你想走多深? – paddy

+2

它看起來是正確的(我沒有去通過你的計算)。你所看到的是構成IEEE-754單精度浮點數的位的無符號整數*等效值*。你可以創建一個簡單的'float'和'uint32_t'的聯合體,並檢查兩者的輸出以確認。 –

+1

你的代碼是好的,雖然它不輪,只能截斷。 – deamentiaemundi

回答

3

從註釋繼續。您的代碼是正確的,您只需查看由您的IEEE-754單精度浮點數中的位組成的等效的unsigned integer。 IEEE-754單精度數字格式(由符號,擴展指數和尾數組成)可以被解釋爲float,或者那些相同的位可以被解釋爲unsigned integer(僅由數字組成的數字) 32位)。您正在輸出無符號等效的作爲浮點數。

您可以通過簡單的聯合來確認。例如:

#include <stdio.h> 
#include <stdint.h> 

typedef union { 
    uint32_t u; 
    float f; 
} u2f; 

int main (void) { 

    u2f tmp = { .f = 2.0 }; 
    printf ("\n u : %u\n f : %f\n", tmp.u, tmp.f); 

    return 0; 
} 

使用示例/輸出

$ ./bin/unionuf 

u : 1073741824 
f : 2.000000 

讓我知道,如果您有任何進一步的問題。很高興看到您的研究能夠實現正確的浮點轉換。(也請注意關於截斷/舍入的第二條評論)

+0

謝謝一堆。我必須做更多的閱讀,但我想我明白了。由於沒有更好的方式來表達,浮點數和未簽名的整數表示兩個不同的故事。所以,他們有兩種不同的規格來描述它們。這也意味着每個的比特級表示將會不同。所以,在打印出來時,我們告訴C要使用哪種規格。我正在告訴程序使用未簽名的版本。另外,我會確保回到舍入部分。這花了一段時間才明白,所以我試圖一次把所有的東西都拿走。 – JustBlossom

+0

是的,你有它。 32位只是32位。如果你通過'float'窗口看它們(例如*符號位*,接着8位*指數*和23位*尾數*),您將看到它們表示爲浮點數,當你查看'unsigned'窗口(考慮位0-31)時,你會得到那些位的'unsigned'值。無論哪種方式,它們都是相同的位。這就是浮動物如何代表我們稱之爲整數的經文。 –

0

我只是在這裏叮叮噹噹,因爲沒有任何關於排序的具體問題已經得到解決。所以我們來談談它。

  1. 在原始問題中,值的構建是使用位移和其他按位運算的不依賴端點的。這意味着無論您的系統是大端還是小端,實際值都是相同的。差異將是它在內存中的字節順序。

  2. IEEE-754普遍接受的慣例是字節順序是big-endian(儘管我相信沒有這個規範,因此沒有要求執行它)。這意味着如果你想直接將你的整數值解釋爲一個浮點數,它需要按照big-endian字節順序進行佈局。

所以,你可以使用這種方式與工會聯合如果且僅當你知道你的系統上,浮點數和整數字節順序是相同

在基於Intel的常見體系結構上,這並不好。在這些架構中,整數是小端,浮點數是大端。你需要將你的價值轉換成大端。一個簡單的方法,這是重新包裝其字節即使他們已經大端

uint32_t n = float_i2f(input_val); 
uint8_t char bytes[4] = { 
    (uint8_t)((n >> 24) & 0xff), 
    (uint8_t)((n >> 16) & 0xff), 
    (uint8_t)((n >> 8) & 0xff), 
    (uint8_t)(n & 0xff) 
}; 
float fval; 
memcpy(&fval, bytes, sizeof(float)); 

我強調的是,你只需要擔心這個問題,如果你想重新詮釋您的整數表示爲一個float或其他方式。

如果您只是試圖輸出表示的位數,那麼您不必擔心。你可以用十六進制等有用形式顯示你的整數:

printf("0x%08x\n", n); 
+0

x86使用小尾數爲浮點數和雙精度值,就像它用於所有值一樣...... –

+0

我的錯誤。我可能對網絡字節順序感到困惑。 – paddy

+0

網絡字節定位是bigendian - 意思是如果你想在x86上以網絡順序傳輸浮動/雙打,你需要字節交換。 –