我正在使用 HIDAPI 将一些数据发送到USB设备。此数据只能作为 byte 数组发送,我需要在此数据数组中发送一些float
个数字。我知道浮点数有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
。我的代码有什么问题,我怎么能正确地做到这一点(即在float
字节中打破4 char
内部数据并在以后重建相同的float
?
修改:
我能够通过以下代码完成此任务:
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()
是一种“更清洁”的方式,但这种方式我觉得性能稍好一些。
答案 0 :(得分:19)
你可以这样做:
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]);
}
,因为您可能不将任意数组的字符视为浮点数!例如:
{{1}}
答案 1 :(得分:7)
在标准C中,可以保证任何类型都可以作为字节数组进行访问。 当然,直接的方法是使用工会:
#include <stdio.h>
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
方法易于理解,标准且快速。
修改强>
我将解释为什么这种方法在 C ( C99 )中有效。
CHAR_BIT
位(整数常数&gt; = 8,在几乎情况下为8)。 unsigned char
类型使用其所有位来表示对象的值,这是一个非负整数,采用纯二进制表示形式。这意味着没有用于任何其他外部purpouse的填充位或位。 (signed char
或char
类型不保证同样的事情。 unsigned char [n]
类型的对象中(例如,通过memcpy); [...]“对象的存储值只能由具有其中一个的左值表达式访问 以下类型:
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的合格版本,
- 对应于有效类型的有符号或无符号类型的类型 对象,
- 对应于合格版本的有符号或无符号类型的类型 有效的对象类型,
- 聚合或联合类型,其中包括上述类型之一 成员(包括,递归地,成为子集合或包含的联合的成员),或者 - 字符类型
更多信息:
A discussion in google groups
Type-punning
编辑2
标准C99的另一个细节:
如果用于访问union对象内容的成员与上次使用的成员不同 在对象中存储一个值,该值的对象表示的相应部分被重新解释 作为6.2.6中描述的新类型中的对象表示(有时称为“类型”的过程) 惩罚“)。这可能是一个陷阱表示。
答案 2 :(得分:1)
C语言保证任何类型的任何值¹都可以作为字节数组进行访问。字节类型为unsigned char
。这是将float复制到字节数组的低级方法。 sizeof(f)
是用于存储变量f
的值的字节数;你也可以使用sizeof(float)
(你可以传递sizeof
一个变量或更复杂的表达式,或者它的类型。)
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;
}
函数memcpy
或memmove
完全相同(或其优化版本)。
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 floating point formats,但您可能需要切换字节顺序。
对于您的尝试有什么问题:>>
运算符对整数进行操作。在表达式(int) f >> 24
中,f
被强制转换为int
;如果您在没有演员的情况下撰写f >> 24
,f
仍会自动转换为int
。将浮点值转换为整数通过截断或舍入(通常朝向0,但规则取决于平台)来近似它。 0.6舍入为整数为0或1,因此data[0]
为0或1,其他均为0。
您需要对float对象的字节进行操作,而不是对其值进行操作。
¹排除在C中无法真正操作的函数,但包括函数指针,函数指针会自动衰减。 子>
答案 3 :(得分:0)
假设两个设备都具有相同的浮点表示概念,那么为什么不做memcpy
。即
unsigned char payload[4];
memcpy(payload, &f, 4);
答案 4 :(得分:0)
最安全的方法是,如果你控制双方都是发送某种标准化的表示......这不是最有效的,但对于小数字来说也不算太糟糕。
hostPort writes char * "34.56\0" byte by byte
client reads char * "34.56\0"
然后使用库函数atof
或atof_l
转换为float。
当然,这不是最优化的,但它确实很容易调试。
如果你想获得更多优化和创意,第一个字节是长度然后是指数,那么每个字节代表2个小数位......所以
34.56
变成char array[] = {4,-2,34,56};
类似于可移植的东西......我只是尝试不传递二进制浮点表示...因为它可能会变得很乱。
答案 5 :(得分:-2)
联合float和char数组可能更安全。放入float成员,拉出4(或任何长度)字节。