从4个复合字节构建一个32位浮点数

Mad*_*eek 21 c++ floating-point portability endianness single-precision

我正在尝试用4个复合字节构建一个32位浮点数.有没有比使用以下方法更好(或更便携)的方法?

#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;
}
Run Code Online (Sandbox Code Playgroud)

ken*_*ytm 30

你可以使用memcpy(结果)

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

或者工会*(结果)

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;
Run Code Online (Sandbox Code Playgroud)

但这并不比你的代码更便携,因为不能保证平台是小端的,或者float是使用IEEE binary32甚至是sizeof(float) == 4.

(注意*:正如@James所解释的那样,在标准(C++§[class.union]/1)中技术上不允许访问union成员u.f.)

  • 要解决`sizeof(float)`问题,你可以将`b`成员声明为`uchar b [sizeof(float)];`. (3认同)

jho*_*oll 16

以下函数以网络字节顺序打包/解压缩表示缓冲区中单个精度浮点值的字节.只有pack方法需要考虑字节序,因为unpack方法显式地构造了来自各个字节的32位值,通过将它们移位适当的量然后将它们进行OR运算.这些函数仅对以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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我很确定由于严格的别名规则,这是 C++ 中未定义的行为。 (2认同)

Jam*_*lis 14

你可以使用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;
} 
Run Code Online (Sandbox Code Playgroud)

这避免了联合黑客攻击,这在语言技术上是不允许的.它还避免了常用的reinterpret_cast<float*>(byte_array),它违反了严格的别名规则(允许将任何对象重新解释为数组char,因此reinterpret_cast此解决方案中的s不违反严格的别名规则).

它仍然依赖于float四个字节的宽度,并依赖于您的实现的浮点格式中的四个字节是有效的浮点数,但您必须做出这些假设,或者您必须编写特殊处理代码来进行转换.


Pat*_*ick 5

没有办法做到这一点便携式,因为不同的平台可以使用:

我还想知道你从哪里得到这4个字节?

如果我假设您从另一个系统获取它们,并且可以保证两个系统使用完全相同的方法在内存中存储浮点值,则可以使用联合技巧。否则,你的代码几乎肯定是不可移植的。