0

I'm currently trying to send floats over USB. The PC has a Qt application running the following code

float x = 2.0;
memcpy(buffer.data() + 14, &x,  sizeof x);

with the debuglog function i can clearly see the series of bytes passing through with the following order

.. 00 00 00 40 ..

which according to this site

http://www.scadacore.com/field-tools/programming-calculators/online-hex-converter/

is converted to 1073741824 in case of little endianess.

on my STM32 i can use the following code to then turn on LED4

uint32_t a = buffer[14] | (buffer[15] << 8) | (buffer[16] << 16) | (buffer[17] << 24);                      
if (a == 1073741824){
    HAL_GPIO_WritePin(LED4_PORT, LED4_PIN, GPIO_PIN_RESET);
}

The problem arises when i want to convert to the real float value 2.0

in fact if i do

float b = buffer[14] | (buffer[15] << 8) | (buffer[16] << 16) | (buffer[17] << 24);
if (b == 2.0f){
    HAL_GPIO_WritePin(LED5_PORT, LED5_PIN, GPIO_PIN_RESET);
}

the LED5 is not turning on.

Is it an endianess problem? How can i obtain the float value 2.0?

Regards,

Luigi
  • 376
  • 3
  • 16
  • 1
    Send the floats as ASCII text and convert them back at the other end. It's just safer and easier. – ThingyWotsit Jun 04 '17 at 11:03
  • "2.0" with a terminating char, (CR or NUL say), is the same size as a single, and cannot be easily misinterpreted ,as long as both sides know the protocol, and then you can communicate most anything with anything. Also, the protocol with the terminating char avoids the problems with streaming connections where an rx call returns 'early' with fewer bytes than needed for a complete message/float. – ThingyWotsit Jun 04 '17 at 11:09
  • can you provide an example? – Luigi Jun 04 '17 at 16:13

1 Answers1

2

For IEEE 754 floats, the endianess is usually not a problem, but check just to be sure.

Your main problem is reading the value. You are ORing value in integer arithmetic and get an integer value, which is correct, but then you convert it to float. This will always give garbage, except for 0.

You need to reinterpret it as float. You can use :

  • A pointer cast : float a = *(float*)&myInt;
  • A memcpy : memcpy(&myFloat, &myInt, 4);
  • A union : union { uint32_t i; float f; };, u.i = myInt; float f = u.f;

You also need to be careful when comparing floating point. It is generally a bad idea to write if( f == 2.0f ), because 2.0f is an approximation. It is better to avoid ==, and use > >= < <= instead. If you want to check a particular value and not a range, check a small enough range around your value, with your range matching your precision needs.

ElderBug
  • 5,926
  • 16
  • 25
  • I'll give it a try andò report it back. Anyway i thought that a given uint32 can only translate to a fixed float. Is it not? – Luigi Jun 04 '17 at 13:42
  • @Luigi Yes, every uint32 can represent a valid, different float. Though, by converting, you can only get a fraction of the float range, and not the ones you want in your case. For example, `1.0f` is represented as `0x3F800000`, an integer that will not give `1.0` if you **convert** it. – ElderBug Jun 04 '17 at 15:03
  • Is there a shortcut to work only/directly with the elements of the array? – Luigi Jun 04 '17 at 16:05
  • @Luigi You can probably use the array directly, but only if the endianess is correct. That would give `float f = *(float*)&buffer[14]`. You can also do that for integer, again only if the endianess is correct. If it isn't, I think you can't do that without an intermediary variable, but you can wrap everything in a function to have a nicer syntax. – ElderBug Jun 04 '17 at 16:24
  • So should i fabs(myFloat - 2.0f) < 0.01f to check whether or not the value is correct? – Luigi Jun 04 '17 at 17:26
  • @Luigi Yes, that is one sensible possibility. – ElderBug Jun 04 '17 at 17:41
  • @Luigi Note that in the case of `2.0`, `==` will work fine because it is exact in float form, but the general idea is to avoid `==` for floats. – ElderBug Jun 04 '17 at 17:47
  • 2
    Notice that only the `memcpy` solution is actually allowed by the standard strict aliasing rules.. – Matteo Italia Jun 04 '17 at 20:34
  • How do i know if my ORing order is the correct one? – Luigi Jun 05 '17 at 10:50
  • Are you sure endianess is unrelated? It seems it is dependant https://stackoverflow.com/questions/2945174/floating-point-endianness @ElderBug – Luigi Jun 05 '17 at 11:20
  • @Luigi That's why I wrote "*usually*". Everything that use multiple bytes for a single value is endianess dependent. Its just that on common modern architecture (x86, AMD64, ARM, ARM64...), the integer endianess is the same for float. As a counter example, this is not true for PowerPC. To know the correct one, you just need to know the endianess of your achitecture. If you don't know, maybe the simplest way is to try both and see which one works. – ElderBug Jun 05 '17 at 13:51