How to get float in bytes?

I use HIDAPI to send some data to a USB device. This data can only be sent as a byte array, and I need to send some numbers with a float inside this data array. I know floats have 4 bytes. So I thought this might work:

 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 was:

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

But testing shows that strings such as data[0] = (int) f >> 24; always returns 0 . What is wrong with my code and how can I do it right (i.e. split internal data from float into 4 char bytes and later rebuild the same with float )?


EDIT :

I was able to do 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 that memcpy() is a cleaner way to do this, but I think the performance is slightly better.

+7
c floating-point
source share
6 answers

You can do it as follows:

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

For this to work, both machines must use the same floating point views.


As was rightly pointed out in the comments, you don't have to do extra memcpy ; instead, you can treat f directly as an array of characters (of any signature). However, you still have to do memcpy on the receiving side, since you cannot process an arbitrary array of characters like a float! Example:

 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]); } 
+19
source share

The C standard guarantees that any type can be accessed by an array of bytes. The direct way to do this is, of course, using unions:

  #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, you don’t need to use memcpy or pointers at all ...

The union approach is easy to understand, standard and fast.

EDIT.

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

  • [5.2.4.2.1 (1)] The byte has the CHAR_BIT bit (integer constant> = 8, in most cases it is 8).
  • [6.2.6.1 (3)] The unsigned char type uses all its bits to represent the value of an object that is a non-negative integer in a pure binary representation. This means that there are no fill bits or bits used for any other extrusion purpura. (The same is not guaranteed for signed char or char types).
  • [6.2.6.1 (2)] Each type without a bit field is represented in memory as a continuous sequence of bytes.
  • [6.2.6.1 (4)] (cited) "Values ​​stored in objects without the bits of any other type of object consist of n × CHAR_BIT bits, where n is the size of an object of this type, in bytes. The value can be copied to the object of type unsigned char [n] (for example, memcpy); [...] "
  • [6.7.2.1 (14)] A pointer to an object of a structure (in particular, a union), appropriately transformed, indicates its initial member. (Thus, there are no padding bytes at the beginning of the union).
  • [6.5 (7)] Access to the contents of an object can be obtained by the type of symbol:

The object must have a stored value, access to which has only an lvalue expression, which has one of the following types:
- a type compatible with the effective type of the object,
- a qualified version of a type compatible with an effective type of object,
- a type that is a signed or unsigned type corresponding to the effective type of an object,
- a type that is a signed or unsigned type corresponding to a quality version is an effective type of object,
- an aggregate or combined type that includes one of the above types among its members (including, recursively, declare a sub-aggregate or a union), or
- type of symbol

Additional Information:

Google group discussion
Model punishment

EDIT 2

Another detail of the C99 standard:

  • [6.5.2.3 (3) footnote 82] The use of the pool is permitted :

If the element used to access the contents of the union object does not coincide with the element that was last used to store the value in the object, the corresponding part of the value object representation is reinterpreted as representing the object in a new type, as described in 6.2.6 (a process sometimes called "type" punning "). This may be a trap view.

+7
source share

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

 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 memcpy or memmove do exactly that (or its optimized version).

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

You do not even need to make this copy. You can directly pass a pointer to a float to the write-to-USB function and specify how many bytes to copy ( sizeof(f) ). You will need an explicit conversion if the function takes a pointer argument other than void* .

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

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


As for your incorrect attempt: the >> operator works with integers. In the expression (int) f >> 24 , f cast to int ; if you wrote f >> 24 without translation, f will still be automatically converted to int . Converting a floating point value to an integer approximates it by trimming or rounding it (usually in the direction of 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 rest is 0.

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

¹ The exception of functions that actually cannot be manipulated in C, but including pointers to functions whose functions automatically decompose. Sub>

+1
source share

Assuming both devices have the same idea of ​​how floats are displayed, why not just make memcpy . i.e

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

the safest way to do this if you control both sides is to send a standard view ... this is not the most efficient, but for small numbers it is not so bad.

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

then converts to float with the atof or atof_l library function.

Of course, not the most optimized, but it will be easy to debug.

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

34.56 becomes char array[] = {4,-2,34,56}; something like this would be portable ... I would just try not to pass binary floating point representations ... because it can get confused quickly.

0
source share

It might be safer to combine the array of float and char. Put in the float element, pull out 4 (or any length) bytes.

-2
source share

All Articles