C语言 如何以字节为单位获得浮点数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21005845/
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-09-02 10:35:35  来源:igfitidea点击:

How to get float in bytes?

cfloating-point

提问by mFeinstein

I am using the HIDAPIto send some data to a USB device. This data can be sent only as bytearray and I need to send some floatnumbers inside this data array. I know floats have 4bytes. So I thought this might work:

我正在使用HIDAPI将一些数据发送到 USB 设备。这个数据只能作为字节数组发送,我需要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;

And later all I had to do is:

后来我所要做的就是:

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

But testing this shows me that the lines like data[0] = (int) f >> 24;returns always 0. What is wrong with my code and how may I do this correctly (i.e. break a floatinner data in 4 charbytes and rebuild the same floatlater)?

但是测试这告诉我像data[0] = (int) f >> 24;return这样的行总是0。我的代码有什么问题,我该如何正确执行此操作(即float4 char字节为单位破坏内部数据并float稍后重建相同的数据)?



EDIT:

编辑

I was able to accomplish this with the following codes:

我能够使用以下代码完成此操作:

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

and

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

I know memcpy()is a "cleaner" way of doing it, but this way I think the performance is somewhat better.

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

回答by Kerrek SB

You can do it like this:

你可以这样做:

char data[sizeof(float)];


float f = 0.6f;

memcpy(data, &f, sizeof f);    // send data


float g;

memcpy(&g, data, sizeof g);    // receive data

In order for this to work, both machines need to use the same floating point representations.

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



As was rightly pointed out in the comments, you don't necessarily need to do the extra memcpy; instead, you can treat fdirectlyas an array of characters (of any signedness). You still have to do memcpyon the receiving side, though, since you may nottreat an arbitrary array of characters as a float! Example:

正如评论中正确指出的那样,您不一定需要做额外的事情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]);
}

回答by pablo1977

In standard C is guaranted that any type can be accessed as an array of bytes. A straight way to do this is, of course, by using unions:

在标准 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();
 }

As you can see, it is not necessary at all to use memcpy or pointers...

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

The unionapproach is easy to understand, standard and fast.

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

EDIT.

编辑。

I will explain why this approach is valid in C(C99).

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

  • [5.2.4.2.1(1)]A byte has CHAR_BITbits (an integer constant >= 8, in almost cases is 8).
  • [6.2.6.1(3)]The unsigned chartype uses all its bits to represent the value of the object, which is an nonnegative integer, in a pure binary representation. This means that there are not padding bits or bits used for any other extrange purpouse. (The same thing is not guaranted for signed charor chartypes).
  • [6.2.6.1(2)]Every non-bitfield type is represented in memory as a contiguous sequence of bytes.
  • [6.2.6.1(4)](Cited) "Values stored in non-bit-?eld objects of any other object type consist of n × CHAR_BIT bits, where n is the size of an object of that type, in bytes. The value may be copied into an object of type unsigned char [n](e.g., by memcpy); [...]"
  • [6.7.2.1(14)]A pointer to a structure object (in particular, unions), suitably converted, points to its initial member. (Thus, there is no padding bytes at the beginning of a union).
  • [6.5(7)]The content of an object can be accessed by a character type:
  • [5.2.4.2.1(1)]一个字节有CHAR_BIT比特(一个整数常量 >= 8,在几乎情况下是 8)。
  • [6.2.6.1(3)]unsigned char类型使用其所有位来表示对象的值,该值是一个非负整数,以纯二进制表示。这意味着没有填充位或用于任何其他扩展目的的位。(同样的事情不保证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)]可以通过字符类型访问对象的内容:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a quali?ed version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a quali?ed version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively,amember of a subaggregate or contained union), or
a character type

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

More information:

更多信息:

A discussion in google groups
Type-punning

谷歌群组中的一次讨论
Type-punning

EDIT 2

编辑 2

Another detail of the standard C99:

标准 C99 的另一个细节:

  • [6.5.2.3(3) footnote 82]Type-punningis allowed:
  • [6.5.2.3(3) 脚注 82]允许打字

If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.

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

回答by Gilles 'SO- stop being evil'

The C language guarantees that any value of any type1 can be accessed as an array of bytes. The type of bytes is unsigned char. Here's a low-level way of copying a float to an array of bytes. sizeof(f)is the number of bytes used to store the value of the variable f; you can also use sizeof(float)(you can either pass sizeofa variable or more complex expression, or its type).

C 语言保证任何类型 1 的任何值都可以作为字节数组访问。字节类型为unsigned char. 这是将浮点数复制到字节数组的低级方法。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;
}

The functions memcpyor memmovedo exactly that (or an optimized version thereof).

功能memcpymemmove完全做到这一点(或其优化版本)。

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

You don't even need to make this copy, though. You can directly pass a pointer to the float to your write-to-USB function, and tell it how many bytes to copy (sizeof(f)). You'll need an explicit cast if the function takes a pointer argument other than void*.

不过,您甚至不需要制作此副本。您可以直接将指向浮点数的指针传递给写入 USB 函数,并告诉它要复制多少字节 ( sizeof(f))。如果函数采用除void*.

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

Note that this will work only if the device uses the same representation of floating point numbers, which is common but not universal. Most machines use the IEEE floating point formats, but you may need to switch endianness.

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



As for what is wrong with your attempt: the >>operator operates on integers. In the expression (int) f >> 24, fis cast to an int; if you'd written f >> 24without the cast, fwould still be automatically converted to an int. Converting a floating point value to an integer approximates it by truncating or rounding it (usually towards 0, but the rule depends on the platform). 0.6 rounded to an integer is 0 or 1, so data[0]is 0 or 1 and the others are all 0.

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

You need to act on the bytes of the float object, not on its value.

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

1 Excluding functions which can't really be manipulated in C, but including function pointers which functions decay to automatically.

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

回答by Grady Player

the safest way to do this, if you control both sides is to send some sort of standardized representation... this isn't the most efficient, but it isn't too bad for small numbers.

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

hostPort writes char * "34.56
unsigned char payload[4];
memcpy(payload, &f, 4);
" byte by byte client reads char * "34.56##代码##"

then converts to float with library function atofor atof_l.

然后使用库函数atofatof_l.

of course that isn't the most optimized, but it sure will be easy to debug.

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

if you wanted to get more optimized and creative, first byte is length then the exponent, then each byte represents 2 decimal places... so

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

34.56becomes char array[] = {4,-2,34,56};something like that would be portable... I would just try not to pass binary float representations around... because it can get messy fast.

34.56变成char array[] = {4,-2,34,56};这样的东西将是可移植的......我只是尽量不传递二进制浮点表示......因为它会很快变得混乱。

回答by Ed Heal

Assuming that both devices have the same notion of how floats are represented then why not just do a memcpy. i.e

假设两个设备对如何表示浮点数具有相同的概念,那么为什么不直接执行memcpy. IE

##代码##

回答by Phil Perry

It might be safer to union the float and char array. Put in the float member, pull out the 4 (or whatever the length is) bytes.

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