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?

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); Commented Sep 7, 2022 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. Commented Sep 7, 2022 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. Commented Sep 7, 2022 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. Commented Sep 7, 2022 at 12:43

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

3 Comments

Perhaps printf("%.8f\n", fc.f); to achieve "float i_motor = 1.02666664"?
@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.
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.
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.

7 Comments

"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: 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.
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 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.
@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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.