简体   繁体   中英

Casting to double pointer on Arm Cortex-M3

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM