1

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?

Dominykas
  • 21
  • 4
  • "int" is often 4 bytes. You should use an array of uint8_t. Try this: `uint8_t array[4] = {0xd0, 0x69, 0x83, 0x3f}; float i_motor = *(float*) array; printf("\n%f\n", i_motor);` – qrsngky Sep 07 '22 at 11:40
  • 3
    @qrsngky **No, don't do that.** Pointer type punning with invalid type is *strict aliasing violation*. It can also cause possible alignment issues. Use `memcpy` instead. – user694733 Sep 07 '22 at 11:41
  • In your question, you first state that the array has elements of type `uint8_t`, but later you write `int array[4]`, which means that the elements are of type `int`. You may want to clarify your question. – Andreas Wenzel Sep 07 '22 at 12:39
  • In your question, you are using long identifiers such as `sdo_rx_data_buffer` and `OD_entry_len` that 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. – Andreas Wenzel Sep 07 '22 at 12:43

2 Answers2

4

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
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Perhaps `printf("%.8f\n", fc.f);` to achieve "float i_motor = 1.02666664"? – chux - Reinstate Monica Sep 07 '22 at 13:30
  • @chux-ReinstateMonica Well that's another story... at that point we might also start to ask if that result is because the underlying `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. – Lundin Sep 07 '22 at 13:38
  • Conversion to `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. – chux - Reinstate Monica Sep 07 '22 at 13:57
3

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.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 2
    "However, this has undefined behavior,because it violates the strict aliasing rule and also may have alignment issues." So why mention it at all, since it is always wrong? – Lundin Sep 07 '22 at 11:47
  • 1
    @Lundin: I mentioned it in order to explain why `memcpy` should be used instead. Also, meanwhile, I added to my answer the possibility of using `__attribute__((__may_alias__))` on gcc and clang. – Andreas Wenzel Sep 07 '22 at 12:50
  • 1
    Strict aliasing is a plain broken language feature however, so it is best to block it in the first place with `-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. – Lundin Sep 07 '22 at 12:53
  • 2
    @Lundin there's a lot of value in including "close but not quite" answers like those, and I'm glad that they're part of this answer. Those answers and the explanations for why they're not good solutions are very helpful for teaching people who may think they are. – romkey Sep 07 '22 at 15:02
  • @romkey Better to list what one should do and then "close but not quite" answers. Further, such poor code should have a comment in code describing it so. It is amazing how many just see the first code and go. – chux - Reinstate Monica Sep 07 '22 at 19:52
  • 1
    @chux-ReinstateMonica well I strongly disagree with you. The answer clearly calls out that those are not good solutions and makes it clear why. Teachable moment. – romkey Sep 07 '22 at 21:51
  • @romkey You think putting in the code a comment about its weakness is bad? Or is it that you disagree with some of the comment? – chux - Reinstate Monica Sep 08 '22 at 02:12