I have uint8_t size 4 integers stored in a array: uint8_t sdo_rx_data_buffer[OD_entry_len];. The length of this array is 4 bytes (OD_entry_len = 4), so is equal to float size (32 bits). I need to convert this array into float variable. For exampe, I have these values:
int array[4] = {0xd0, 0x69, 0x83, 0x3f};
And i should get
float i_motor = 1.02666664;
Any ideas how to convert it?
2 Answers
Assuming that you know that the binary representation gives a valid floating point number on your system and you got the endianess right, then you can use a union:
#include <stdint.h>
#include <stdio.h>
typedef union
{
uint8_t raw [sizeof(float)];
float f;
} float_converter;
int main (void)
{
float_converter fc = { .raw = {0xd0, 0x69, 0x83, 0x3f} };
printf("%f\n", fc.f);
}
Output on x86_64 Linux:
1.026667
3 Comments
printf("%.8f\n", fc.f); to achieve "float i_motor = 1.02666664"?float has that level of precision or because of the implicit promotion to double ("default argument promotions") required to use %f which is actually for double type.double is not part of the issue. Printing 9 significant digits (FLT_DECIMAL_DIG) for common float is what is needed to distinguish all float. There are 9 different float with the "%f" output of 1.026667. Printing more precision helps to demonstrate your good approach worked well.One incorrect way which may seem to work would be to reinterpret the array using a pointer.
uint8_t array[4] = {0xd0, 0x69, 0x83, 0x3f};
float *p = (float*)array;
printf( "%f\n", *p );
However, this code has undefined behavior, because it violates the strict aliasing rule. It may also have alignment issues.
On the compilers gcc and clang, you can use __attribute__((__may_alias__)) on the pointer p, so that there is no strict aliasing violation:
uint8_t array[4] = {0xd0, 0x69, 0x83, 0x3f};
float __attribute__((__may_alias__)) *p = (float*)array;
printf( "%f\n", *p );
However, there still may be alignment issues.
A different way, which fully complies with the ISO C standard (and therefore should work on all compilers), would be to use memcpy instead:
uint8_t array[4] = {0xd0, 0x69, 0x83, 0x3f};
float f;
memcpy( &f, array, sizeof f );
printf( "%f\n", f );
Most compilers will optimize away the memcpy when compiler optimizations are active.
Beware of endianness issues, though. The posted code will only work on little-endian platforms.
7 Comments
memcpy should be used instead. Also, meanwhile, I added to my answer the possibility of using __attribute__((__may_alias__)) on gcc and clang.-fno-strict-aliasing, which I think only works in gcc and not clang (gcc historically being the most broken compiler in this regard). Also, dragging in non-standard gcc extensions into your code for no good reason is simply bad practice. Your memcpy example is fine, there is no need for the first part of the answer at all, in my opinion.
uint8_t array[4] = {0xd0, 0x69, 0x83, 0x3f}; float i_motor = *(float*) array; printf("\n%f\n", i_motor);memcpyinstead.uint8_t, but later you writeint array[4], which means that the elements are of typeint. You may want to clarify your question.sdo_rx_data_bufferandOD_entry_lenthat have a description that does not mean much to the reader. You may want to simplify your question by making these identifiers shorter and more meaningful to the reader.