I am using an Arm Cortex-M3 processor. I receive binary data in an unsigned char
array, which must be cast into a suitable variable to be used for further computation:
unsigned char gps[24] = { 0xFA, 0x05, 0x08, 0x00, 0x10, 0x00,0xA4, 0x15, 0x02, 0x42, 0x4D, 0xDF, 0xEB, 0x3F, 0xF6, 0x1A, 0x36, 0xBE, 0xBF, 0x2D, 0xA4, 0x41,
0xAF, 0x1A };
int i = 6;
float f = (float) *(double*)&gps[i];
This code works on a computer to get the correct value of "f" but it fails on the Cortex-M3. I understand that does not have an arithmetic unit on the processor, hence doesn't support 64 bit operations; but is there a work-around to cast as shown above.
Note that the code below works on the processor; only the casting shown above fails:
double d = 9.7;
Also note that 32 bit casts work, as shown below; only double
or uint64_t
fail.
uint16_t k = *(uint16_t*)&gps[i];
Is there an alternative solution?
Casting the address of an unsigned char
to a pointer to a double
– and then using it – is violating strict aliasing rules ; more importantly (in your case, as discussed below), it also breaks the required alignment rules for accessing multi-byte (ie double
) data units.
Many compilers will warn about this; clang-cl gives the following for the (double*)&gps[i]
expression:
warning : cast from 'unsigned char *' to 'double *' increases required alignment from 1 to 8 [-Wcast-align]
Now, some architectures aren't too fussy about alignment of data types, and the code may (seem to) work on many of those. However, the Cortex-M3 is very fussy about the alignment requirements for multi-byte data types (such as double
).
To remove undefined behaviour, you should use the memcpy
function to transfer the component bytes of your gps
array into a real double
variable, then cast that to a float
:
unsigned char gps[24] = {0xFA, 0x05, 0x08, 0x00, 0x10, 0x00,0xA4, 0x15, 0x02, 0x42, 0x4D,
0xDF, 0xEB, 0x3F, 0xF6, 0x1A, 0x36, 0xBE, 0xBF, 0x2D, 0xA4, 0x41, 0xAF, 0x1A };
int i = 6;
double d; // This will be correctly aligned by the compiler
memcpy(&d, &gps[i], sizeof(double)); // This doesn't need source alignment
float f = (float)d; // ... so now, this is a 'safe' cast down to single precision
The memcpy
call will use (or generate) code that can access unaligned data safely – even if that means a significant slow-down of the access.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.