C++ 从 4 个复合字节中构建 32 位浮点数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3991478/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:13:34  来源:igfitidea点击:

Building a 32-bit float out of its 4 composite bytes

c++floating-pointendiannessportabilitysingle-precision

提问by Madgeek

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?

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

回答by kennytm

You could use a memcpy(Result)

你可以使用memcpy(结果)

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 floatis using IEEE binary32 or even sizeof(float) == 4.

但这并不比您的代码更具可移植性,因为不能保证平台是小端或float使用 IEEE binary32 甚至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.)

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

回答by jholl

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-1985floating point implementations.

以下函数将表示单精度浮点值的字节打包/解包到/从网络字节顺序的缓冲区。只有 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;
}

回答by James McNellis

You can use std::copy:

您可以使用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).

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

It still relies on floatbeing 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.

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

回答by Patrick

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

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

I also wonder where you get these 4 bytes from?

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

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.

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

回答by JoshD

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