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

发布于 2024-09-28 16:08:23 字数 650 浏览 4 评论 0原文

我正在尝试用 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;
}

I'm trying to build a 32-bit float out of its 4 composite bytes. Is there a better (or more portable) way to do this than with the following method?

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

东风软 2024-10-05 16:08:23

您可以使用 memcpy (Result)

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

或 union* (结果

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;

但这并不比您的代码更可移植,因为不能保证该平台是小端字节序或float 使用 IEEE binary32 甚至 sizeof(float) == 4

(注*:如 @James,从技术上讲,标准(C++ §[class.union]/1)不允许访问联合成员uf。)

You could use a memcpy (Result)

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

or a union* (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;

But this is no more portable than your code, since there is no guarantee that the platform is little-endian or the float is using IEEE binary32 or even sizeof(float) == 4.

(Note*: As explained by @James, it is technically not allowed in the standard (C++ §[class.union]/1) to access the union member u.f.)

梦魇绽荼蘼 2024-10-05 16:08:23

以下函数将表示单精度浮点值的字节按网络字节顺序打包/解包到缓冲区或从缓冲区中解包。只有 pack 方法需要考虑字节顺序,因为 unpack 方法通过将各个字节移位适当的量,然后将它们组合在一起,显式地从各个字节构造 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;
}

The following functions pack/unpack bytes representing a single precision floating point value to/from a buffer in network byte order. Only the pack method needs to take endianness into account since the unpack method explicitly constructs the 32-bit value from the individual bytes by bit shifting them the appropriate amount and then OR-ing them together. These functions are only valid for C/C++ implementations that store a float in 32-bits. This is true for IEEE 754-1985 floating point implementations.

// 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;
}
我不是你的备胎 2024-10-05 16:08:23

您可以使用 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(byte_array),它违反了严格的别名规则(允许将任何对象重新解释为 char 数组,因此此解决方案中的reinterpret_cast不会违反严格的别名规则)。

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

You can use 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;
} 

This avoids the union hack, which isn't technically allowed by the language. It also avoids the commonly used reinterpret_cast<float*>(byte_array), which violates the strict aliasing rules (it is permitted to reinterpret any object as an array of char, so the reinterpret_casts in this solution do not violate the strict aliasing rules).

It still relies on float being four bytes in width and relies on your four bytes being a valid floating point number in your implementation's floating point format, but you either have to make those assumptions or you have to write special handling code to do the conversion.

反话 2024-10-05 16:08:23

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

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

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

There's no way to do this portable, since different platforms can use:

  • different byte ordering (big endian vs. little endian)
  • different representations for floating point values (see http://en.wikipedia.org/wiki/IEEE_754-1985 for an example)
  • different sizes for floating point values

I also wonder where you get these 4 bytes from?

If I assume that you get them from another system, and you can guarantee that both systems use exactly the same method to store floating-point values in memory, you can use the union trick. Otherwise, your code is almost guaranteed to be non-portable.

︶ ̄淡然 2024-10-05 16:08:23

如果您想要一种可移植的方式来执行此操作,则必须编写一些代码来检测系统的字节顺序。

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;
}

If you want a portable way to do this, you'll have to write a bit of code to detect the endianess of the system.

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;
}
乱了心跳 2024-10-05 16:08:23

我通常在 C 中使用它——不需要 memcpyunion。我不知道它可能会违反 C++ 中的别名规则。

float bytesToFloat(uint8_t *bytes, bool big_endian) {
    float f;
    uint8_t *f_ptr = (uint8_t *) &f;
    if (big_endian) {
        f_ptr[3] = bytes[0];
        f_ptr[2] = bytes[1];
        f_ptr[1] = bytes[2];
        f_ptr[0] = bytes[3];
    } else {
        f_ptr[3] = bytes[3];
        f_ptr[2] = bytes[2];
        f_ptr[1] = bytes[1];
        f_ptr[0] = bytes[0];
    }
    return f;
}

如果您有一个完整的字节数组需要重新解释为浮点数,则如有必要,您可以对数组中每个连续的 4 个字节序列调用以下过程,以切换字节顺序(例如,如果您运行在小端机器,但字节按大端顺序)。然后,您可以简单地将 uint8_t * 数组指针转换为 float *,并以浮点数数组的形式访问内存。

void switchEndianness(uint8_t *bytes) {
    uint8_t b0 = bytes[0];
    uint8_t b1 = bytes[1];
    uint8_t b2 = bytes[2];
    uint8_t b3 = bytes[3];
    bytes[0] = b3;
    bytes[1] = b2;
    bytes[2] = b1;
    bytes[3] = b0;
}

I typically use this in C -- no memcpy or union required. It may break aliasing rules in C++, I don't know.

float bytesToFloat(uint8_t *bytes, bool big_endian) {
    float f;
    uint8_t *f_ptr = (uint8_t *) &f;
    if (big_endian) {
        f_ptr[3] = bytes[0];
        f_ptr[2] = bytes[1];
        f_ptr[1] = bytes[2];
        f_ptr[0] = bytes[3];
    } else {
        f_ptr[3] = bytes[3];
        f_ptr[2] = bytes[2];
        f_ptr[1] = bytes[1];
        f_ptr[0] = bytes[0];
    }
    return f;
}

If you have a whole array of bytes that need to be re-interpreted as floats, you can call the following procedure for each consecutive sequence of 4 bytes in the array if necessary, to switch the byte order (e.g. if you are running on a little endian machine, but the bytes are in big endian order). Then you can simply cast the uint8_t * array pointer to float *, and access the memory as an array of floats.

void switchEndianness(uint8_t *bytes) {
    uint8_t b0 = bytes[0];
    uint8_t b1 = bytes[1];
    uint8_t b2 = bytes[2];
    uint8_t b3 = bytes[3];
    bytes[0] = b3;
    bytes[1] = b2;
    bytes[2] = b1;
    bytes[3] = b0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文