如何以字节为单位浮点数?

时间:2014-01-08 20:30:00

标签: c floating-point

我正在使用 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()是一种“更清洁”的方式,但这种方式我觉得性能稍好一些。

6 个答案:

答案 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 )中有效。

  • [5.2.4.2.1(1)] 一个字节有CHAR_BIT位(整数常数&gt; = 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是a的大小该类型的对象,以字节为单位。该值可以复制到unsigned char [n]类型的对象中(例如,通过memcpy); [...]“
  • [6.7.2.1(14)] 指向适当转换的结构对象(特别是联合)的指针指向其初始成员。 (因此,在联合的开头没有填充字节)。
  • [6.5(7)] 可以通过字符类型访问对象的内容:
  

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

更多信息:

A discussion in google groups
  Type-punning

编辑2

标准C99的另一个细节:

  • [6.5.2.3(3)脚注82] 允许输入类型
  

如果用于访问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;
}

函数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 floating point formats,但您可能需要切换字节顺序。


对于您的尝试有什么问题:>>运算符对整数进行操作。在表达式(int) f >> 24中,f被强制转换为int;如果您在没有演员的情况下撰写f >> 24f仍会自动转换为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" 

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

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

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

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

答案 5 :(得分:-2)

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