2010-10-21 87 views
18

我試圖從其4個複合字節中構建一個32位浮點。有沒有更好的(或更便攜)的方式來做到這一點比用下面的方法?從其4個複合字節中構建一個32位浮點[C++]

#include <iostream> 

typedef unsigned char uchar; 

float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    float output; 

    *((uchar*)(&output) + 3) = b0; 
    *((uchar*)(&output) + 2) = b1; 
    *((uchar*)(&output) + 1) = b2; 
    *((uchar*)(&output) + 0) = b3; 

    return output; 
} 

int main() 
{ 
    std::cout << bytesToFloat(0x3e, 0xaa, 0xaa, 0xab) << std::endl; // 1.0/3.0 
    std::cout << bytesToFloat(0x7f, 0x7f, 0xff, 0xff) << std::endl; // 3.4028234 × 10^38 (max single precision) 

    return 0; 
} 
+3

考慮到這是我在Stack Overflow上的第一個問題,我對各種迴應感到興奮。我感謝大家的意見。 – Madgeek 2010-10-21 23:47:59

回答

26

你可以使用一個memcpyResult

float f; 
uchar b[] = {b3, b2, b1, b0}; 
memcpy(&f, &b, sizeof(f)); 
return f; 

或工會*(Result

union { 
    float f; 
    uchar b[4]; 
} u; 
u.b[3] = b0; 
u.b[2] = b1; 
u.b[1] = b2; 
u.b[0] = b3; 
return u.f; 

但是,這不是比你的代碼的可移植性,因爲沒有保證平臺是小端的,或者使用IEEE binary32甚至sizeof(float) == 4

(注*:由於@James解釋的那樣,它在技術上不是在標準允許的(C++§[class.union]/1)以訪問聯合成員u.f。)

+3

爲了解決'sizeof(float)'問題,你可以將'b'成員聲明爲'uchar b [sizeof(float)];''。 – 2010-10-21 20:35:50

+0

@Matteo:對,但輸入也需要修改。 – kennytm 2010-10-21 20:40:00

12

可以使用std::copy

float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    uchar byte_array[] = { b3, b2, b1, b0 }; 
    float result; 
    std::copy(reinterpret_cast<const char*>(&byte_array[0]), 
       reinterpret_cast<const char*>(&byte_array[4]), 
       reinterpret_cast<char*>(&result)); 
    return result; 
} 

這樣可以避免聯合黑客攻擊,這在技術上不受該語言的限制。它還避免了常用的reinterpret_cast<float*>(byte_array),它違反了嚴格的別名規則(允許將任何對象重新解釋爲數組char,因此該解決方案中的reinterpret_cast不違反嚴格的別名規則)。

它仍然依賴於float的寬度爲四個字節,並且依賴於您的四個字節是您實現的浮點格式中的有效浮點數,但是您必須做出這些假設或者必須編寫特殊的處理代碼做轉換。

+0

這仍然不是便攜式的,對吧?我只是好奇... – JoshD 2010-10-21 20:29:27

+0

@JoshD:不;它仍然依賴'sizeof(float)== 4',並沒有考慮端到端的問題。它只是避免'reinterpret_cast (some_uchar_array)'和工會黑客。 – 2010-10-21 20:30:49

+0

我相當肯定''reinterpret_cast (byte_array)'必須被允許,如果byte_array(1)正確對齊和(2)實際上包含一個浮點數。我這麼認爲是因爲否則,將'memcpy'作爲'float'到另一個'float'是不可能的(因爲'memcpy'寫入到一個字節數組中),然而'float'是典型的POD類型。 – MSalters 2010-10-22 10:55:11

3

如果你想要一個可移植的方式來做到這一點,你必須編寫一些代碼來檢測系統的永久性。

float bytesToFloatA(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    float output; 

    *((uchar*)(&output) + 3) = b0; 
    *((uchar*)(&output) + 2) = b1; 
    *((uchar*)(&output) + 1) = b2; 
    *((uchar*)(&output) + 0) = b3; 

    return output; 
} 


float bytesToFloatB(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    float output; 

    *((uchar*)(&output) + 3) = b3; 
    *((uchar*)(&output) + 2) = b2; 
    *((uchar*)(&output) + 1) = b1; 
    *((uchar*)(&output) + 0) = b0; 

    return output; 
} 

float (*correctFunction)(uchar b0, uchar b1, uchar b2, uchar b3) = bytesToFloatA; 

if ((*correctFunction)(0x3e, 0xaa, 0xaa, 0xab) != 1.f/3.f) // horrifying, I know 
{ 
    correctFunction = bytesToFloatB; 
} 
+1

這在任何endians中都是不相等的,因爲'1./3.'是一個'double',而不是'float'。你應該使用類似'1.0f/3'的東西。 – kennytm 2010-10-21 20:39:23

4

有沒有辦法做到這一點便攜,因爲不同的平臺可以使用:

我也想知道你從哪裏得到這4個字節?

如果我假設你從另一個系統獲得它們,並且你可以保證這兩個系統使用完全相同的方法在存儲器中存儲浮點值,你可以使用聯合技巧。否則,你的代碼幾乎保證不可移植。

13

以下函數按網絡字節順序將表示單精度浮點值的字節打包到/從緩衝區中解壓縮字節。只有pack方法需要考慮字節順序,因爲unpack方法通過逐位移位適當的數量,然後將它們OR(或)在一起來顯式地從各個字節構造32位值。這些函數僅適用於存儲32位浮點數的C/C++實現。這對於IEEE 754-1985浮點實現來說是正確的。

// unpack method for retrieving data in network byte, 
// big endian, order (MSB first) 
// increments index i by the number of bytes unpacked 
// usage: 
// int i = 0; 
// float x = unpackFloat(&buffer[i], &i); 
// float y = unpackFloat(&buffer[i], &i); 
// float z = unpackFloat(&buffer[i], &i); 
float unpackFloat(const void *buf, int *i) { 
    const unsigned char *b = (const unsigned char *)buf; 
    uint32_t temp = 0; 
    *i += 4; 
    temp = ((b[0] << 24) | 
      (b[1] << 16) | 
      (b[2] << 8) | 
      b[3]); 
    return *((float *) temp); 
} 

// pack method for storing data in network, 
// big endian, byte order (MSB first) 
// returns number of bytes packed 
// usage: 
// float x, y, z; 
// int i = 0; 
// i += packFloat(&buffer[i], x); 
// i += packFloat(&buffer[i], y); 
// i += packFloat(&buffer[i], z); 
int packFloat(void *buf, float x) { 
    unsigned char *b = (unsigned char *)buf; 
    unsigned char *p = (unsigned char *) &x; 
#if defined (_M_IX86) || (defined (CPU_FAMILY) && (CPU_FAMILY == I80X86)) 
    b[0] = p[3]; 
    b[1] = p[2]; 
    b[2] = p[1]; 
    b[3] = p[0]; 
#else 
    b[0] = p[0]; 
    b[1] = p[1]; 
    b[2] = p[2]; 
    b[3] = p[3]; 
#endif 
    return 4; 
} 
+0

我認爲在代碼行中有一個錯誤:return *((float *)temp); 應該是:return *((float *)&temp); – 2017-04-25 08:43:10

相關問題