2010-10-21 44 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

相关问题