C++ 从4个复合字节中构建32位浮点
我试图用它的4个复合字节构建一个32位浮点。有没有比使用以下方法更好(或更便于携带)的方法C++ 从4个复合字节中构建32位浮点,c++,floating-point,endianness,portability,single-precision,C++,Floating Point,Endianness,Portability,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)
#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;
}
#包括
typedef无符号字符;
通过测试浮点数(uchar b0、uchar b1、uchar b2、uchar b3)
{
浮动输出;
*((uchar*)(&output)+3)=b0;
*((uchar*)(&output)+2)=b1;
*((uchar*)(&output)+1)=b2;
*((uchar*)(&output)+0)=b3;
返回输出;
}
int main()
{
std::cout您可以使用memcpy
()
或工会*()
但这并不比您的代码更具可移植性,因为无法保证平台是little endian或float
使用IEEE binary32,甚至sizeof(float)==4
(注*:如所述,标准(C++§[class.union]/1)技术上不允许访问工会成员u.f
)您可以使用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;
}
float bytesToFloat(uchar b0、uchar b1、uchar b2、uchar b3)
{
uchar字节_数组[]={b3,b2,b1,b0};
浮动结果;
std::copy(重新解释转换(&字节数组[0]),
重新解释字节转换(&字节数组[4]),
重新解释铸造(结果));
返回结果;
}
这避免了该语言在技术上不允许的联合攻击。它还避免了常用的重新解释转换(字节数组)
,这违反了严格的别名规则(允许将任何对象重新解释为char
数组,因此此解决方案中的reinterpret\u cast
s不会违反严格的别名规则)
它仍然依赖于float
是四个字节的宽度,并且依赖于您的四个字节是实现的浮点格式中的有效浮点数,但是您必须做出这些假设,或者您必须编写特殊的处理代码来进行转换。如果您想要一种可移植的方式来实现这一点,您必须编写一段代码来检测系统的持久性
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;
}
由于不同的平台可以使用:
- 不同的字节顺序(大端与小端)
- 浮点值的不同表示形式(请参见示例)
- 浮点值的不同大小
我还想知道这4个字节是从哪里来的
如果我假设您从另一个系统获取它们,并且您可以保证两个系统使用完全相同的方法在内存中存储浮点值,那么您可以使用union技巧。否则,您的代码几乎可以保证是不可移植的。以下函数打包/解包表示单精度浮点点的字节以网络字节顺序向缓冲区发送/从缓冲区发送的t值。只有pack方法需要考虑endianness,因为unpack方法通过将单个字节位移位适当的量,然后将它们合并在一起,从而显式地从单个字节构造32位值。这些函数仅对存储浮点值的C/C++实现有效32位。这对于浮点实现是正确的
// 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;
}
//用于检索网络字节中数据的解包方法,
//大端号,顺序(MSB优先)
//按未打包的字节数递增索引i
//用法:
//int i=0;
//float x=解包float(&buffer[i],&i);
//浮动y=浮动(&缓冲区[i],&i);
//float z=解包float(&buffer[i],&i);
浮点数解压浮点数(常数void*buf,int*i){
常量无符号字符*b=(常量无符号字符*)buf;
uint32温度=0;
*i+=4;
(b(0)我通常在C中使用这一点——没有<代码> MeMCPY 或 CUng/<代码>。它可能会破坏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\u t*
数组指针强制转换为float*
,并以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;
}
@JoshD:没有;它仍然依赖于sizeof(float)==4
,并且没有考虑端点。它只是避免了reinterpret\u cast(一些数组)
和union hack。我相当肯定,如果字节数组(1)正确对齐,那么必须允许reinterpret\u cast(字节数组)
实际上包含一个浮点数。我这样认为是因为,否则就不可能将memcpy
afloat
写入另一个float
(因为memcpy
写入字节数组),而float
是典型的POD类型。@MSalters:但是memcpy
不会将字节数组重新解释为float;它会将float重新解释为字节数组。它确实不是memcpy
本身;它显然只在字节数组上起作用。这保证了您可以将输出字节数组用作float。要解决sizeof(float)
问题您可以将b
成员声明为uchar b[sizeof(float)]
@Matteo:是的,但是输入也需要修改。这在任何尾数中都是不相等的,因为1./3。
是双精度,而不是浮点数。你应该使用类似1.0f/3
。考虑到这是我关于堆栈溢出的第一个问题,我对各种回答感到兴奋。谢谢你对于输入,我认为代码行中有一个错误:返回*((浮点*)临时参数);它应该是:返回*((浮点*)和临时参数);我很确定,这是由于严格的混叠规则在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;
}
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;
}