如何获取浮点字节?

我正在使用HIDAPI将一些数据发送到USB设备。 这个数据只能作为字节数组发送,我需要在这个数据数组中发送一些浮点数。 我知道浮点数有4个字节所以我认为这可能有效:

float f = 0.6; char data[4]; data[0] = (int) f >> 24; data[1] = (int) f >> 16; data[2] = (int) f >> 8; data[3] = (int) f; 

后来我所要做的就是:

 g = (float)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]) ); 

但测试这显示了像data[0] = (int) f >> 24; 总是返回0 。 我的代码有什么问题,我怎么能正确地做到这一点(即以4个字节字节中断一个浮动内部数据并在以后重建同一个浮点数)?

编辑:

我能够用以下代码完成此任务:

 float f = 0.1; unsigned char *pc; pc = (unsigned char*)&f; // 0.6 in float pc[0] = 0x9A; pc[1] = 0x99; pc[2] = 0x19; pc[3] = 0x3F; std::cout << f << std::endl; // will print 0.6 

 *(unsigned int*)&f = (0x3F << 24) | (0x19 << 16) | (0x99 << 8) | (0x9A << 0); 

我知道memcpy()是一种“更清洁”的方式,但这种方式我认为性能稍好一些。

你可以这样做:

 char data[sizeof(float)]; float f = 0.6f; memcpy(data, &f, sizeof f); // send data float g; memcpy(&g, data, sizeof g); // receive data 

为了使其工作,两台机器都需要使用相同的浮点表示。


正如评论中正确指出的那样,你不一定需要额外的memcpy ; 相反,您可以将f 直接视为一个字符数组(任何签名)。 但是,你仍然需要在接收端进行memcpy ,因为你可能不会将任意数组的字符视为浮点数! 例:

 unsigned char const * const p = (unsigned char const *)&f; for (size_t i = 0; i != sizeof f; ++i) { printf("Byte %zu is %02X\n", i, p[i]); send_over_network(p[i]); } 

在标准C中,保证任何类型都可以作为字节数组进行访问。 当然,直接的方法是使用工会:

  #include  int main(void) { float x = 0x1.0p-3; /* 2^(-3) in hexa */ union float_bytes { float val; unsigned char bytes[sizeof(float)]; } data; data.val = x; for (int i = 0; i < sizeof(float); i++) printf("Byte %d: %.2x\n", i, data.bytes[i]); data.val *= 2; /* Doing something with the float value */ x = data.val; /* Retrieving the float value */ printf("%.4f\n", data.val); getchar(); } 

如您所见,完全没有必要使用memcpy或指针......

union方法易于理解,标准且快速。

编辑。

我将解释为什么这种方法在CC99 )中有效。

  • [5.2.4.2.1(1)]一个字节有CHAR_BIT位(整数常数> = 8,在几乎情况下为8)。
  • [6.2.6.1(3)] unsigned char类型使用其所有位来表示对象的值,它是纯二进制表示中的非负整数。 这意味着没有用于任何其他插入purpouse的填充位或位。 (对于signed charchar类型,不保证同样的事情)。
  • [6.2.6.1(2)]每个非位域类型在存储器中表示为连续的字节序列。
  • [6.2.6.1(4)] (引用)“存储在任何其他对象类型的非位字段对象中的值由n×CHAR_BIT位组成,其中n是该类型对象的大小(以字节为单位)。可以复制到unsigned char [n]类型的对象中(例如,通过memcpy); [...]“
  • [6.7.2.1(14)]指向适当转换的结构对象(特别是联合)的指针指向其初始成员。 (因此,在联合的开头没有填充字节)。
  • [6.5(7)]可以通过字符类型访问对象的内容:

对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的限定版本,
- 对应于对象的有效类型的有符号或无符号类型,
- 对应于对象有效类型的限定版本的有符号或无符号类型,
- 聚合或联合类型,其成员中包含上述类型之一(包括递归地,子聚合或包含联合的成员),或者
- 一个字符类型

更多信息:

谷歌小组讨论
型双关语

编辑2

标准C99的另一个细节:

  • [6.5.2.3(3)脚注82]允许打字

如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为新类型中的对象表示forms在6.2.6中描述(一个过程有时称为“类型双关语”)。 这可能是陷阱表示。

C语言保证任何类型的任何值¹都可以作为字节数组访问。 字节类型是unsigned char 。 这是将float复制到字节数组的低级方法。 sizeof(f)是用于存储变量f的值的字节数; 你也可以使用sizeof(float) (你可以传递一个变量或更复杂的表达式的大小,或者它的类型)。

 float f = 0.6; unsigned char data[sizeof(float)]; size_t i; for (i = 0; i < sizeof(float); i++) { data[i] = (unsigned char*)f + i; } 

memcpymemmove函数正是这样做的(或其优化版本)。

 float f = 0.6; unsigned char data[sizeof(float)]; memcpy(data, f, sizeof(f)); 

但是,您甚至不需要制作此副本。 您可以直接将指向float的指针传递给您的写入USB函数,并告诉它要复制多少字节( sizeof(f) )。 如果函数采用void*之外的指针参数,则需要显式转换。

 int write_to_usb(unsigned char *ptr, size_t size); result = write_to_usb((unsigned char*)f, sizeof(f)) 

请注意,这仅在设备使用相同的浮点数表示时才有效,这是常见的但不是通用的。 大多数机器使用IEEE浮点格式 ,但您可能需要切换字节序。


至于你的尝试有什么问题: >>运算符对整数运算。 在表达式(int) f >> 24f被强制转换为int ; 如果您在没有强制转换的情况下编写f >> 24 ,则f仍会自动转换为int 。 将浮点值转换为整数通过截断或舍入(通常朝向0,但规则取决于平台)来近似它。 0.6舍入为整数为0或1,因此data[0]为0或1,其他均为0。

您需要对float对象的字节进行操作,而不是对其值进行操作。

¹ 排除在C中无法真正操作的函数,但包括函数指针,函数指针会自动衰减。

假设两个设备具有相同的浮点数表示概念,那么为什么不只是做一个memcpy 。 即

 unsigned char payload[4]; memcpy(payload, &f, 4); 

最安全的方法,如果你控制双方是发送某种标准化的表示……这不是最有效的,但对于小数字来说并不是太糟糕。

 hostPort writes char * "34.56\0" byte by byte client reads char * "34.56\0" 

然后使用库函数atofatof_l转换为float。

当然,这不是最优化的,但它肯定很容易调试。

如果你想获得更多的优化和创造性,第一个字节的长度是指数,那么每个字节代表2个小数位……所以

34.56变为char array[] = {4,-2,34,56}; 类似的东西是可移植的……我只是试着不要传递二进制浮点表示…因为它可能会变得很乱。

联合float和char数组可能更安全。 放入浮动成员,拉出4(或任何长度)字节。