I have been struggling with finding a portable way to serialize 32-bit float variables in C and C++ to be sent to and from microcontrollers. I want the format to be well-def
You seem to have a bug in serialize_float
: the last 4 lines should read:
buffer[(*index)++] = (res >> 24) & 0xFF;
buffer[(*index)++] = (res >> 16) & 0xFF;
buffer[(*index)++] = (res >> 8) & 0xFF;
buffer[(*index)++] = res & 0xFF;
Your method might not work correctly for infinities and/or NaNs because of the offset by 126
instead of 128
. Note that you can validate it by extensive testing: there are only 4 billion values, trying all possibilities should not take very long.
The actual representation in memory of float
values may differ on different architectures, but IEEE 854 (or more precisely IEC 60559) is largely prevalent today. You can verify if your particular targets are compliant or not by checking if __STDC_IEC_559__
is defined. Note however that even if you can assume IEEE 854, you must handle potentially different endianness between the systems. You cannot assume the endianness of float
s to be the same as that of integers for the same platform.
Note also that the simple cast would be incorrect: uint32_t res = *(uint32_t *)&number;
violates the strict aliasing rule. You should either use a union
or use memcpy(&res, &number, sizeof(res));
Assuming the float is in IEEE 754 format, extracting the mantissa, exponent and sign, is completely portable:
uint32_t internal;
float value = //...some value
memcpy( &internal , &value , sizeof( value ) );
const uint32_t sign = ( internal >> 31u ) & 0x1u;
const uint32_t mantissa = ( internal >> 0u ) & 0x7FFFFFu;
const uint32_t exponent = ( internal >> 23u ) & 0xFFu;
Invert the procedure to construct the float.
If you want to send the entire float only, then just copy it to the buffer. This will work even if float is not IEEE 754, but it must be 32 bit and the endianess of both integer and floating point types must be the same:
buffer[0] = ( internal >> 0u ) & 0xFFu;
buffer[1] = ( internal >> 8u ) & 0xFFu;
buffer[2] = ( internal >> 16u ) & 0xFFu;
buffer[3] = ( internal >> 24u ) & 0xFFu;