[SOLVED] float, byte-order, endianess

Dear All,

I need to transmit floats through an RS-485 connection. I have to send the float in the IEEE754 format as four bytes:

byte 1: SEEEEEEE
byte 2: EMMMMMMM
byte 3: MMMMMMMM
byte 4: MMMMMMMM

With: S = sign bit E = exponant bits M = Mantissa bits

The code is expected to run on ATmega32u4 (Leonardo), ATmega328 (Uno), ATmega328P (Duemilanove), Atmel SAM3X8E ARM Cortex-M3 (Due), PIC32MX320F128 (Chipkit Uno32), PIC32MX340F512H (Chipkit uC32), PIC32MX795F512 (chip kit Max32).

I believe that on all these platforms, the float is 32-bits. So I only need to figure out the endianess of the platform and either declare a union :

union {
    float number;
    byte[4] number_bytes;
}

or cast an array:

byte* number_bytes = (byte*)&number;

My issue is:

How do I know, at compile-time, the endianess of the platform, so that I can act properly?

Thank you for your help.

They’re all little-endian.
In theory, modern gcc defines assorted predefined symbols:

 /usr/local/CrossPack-AVR-20121207/bin/avr-gcc -dM -E - < /dev/null | grep -i end
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __ORDER_PDP_ENDIAN__ 3412
#define __ORDER_BIG_ENDIAN__ 4321
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

However, the gcc used with arduino, due, and mpide doesn’t seem to support those :frowning:

Thank you very much for your reply.

I figured out, the safest way to do it, is for me to detect endianess at runtime during RS-485 protocol class initialization. It adds a little more code, is a little bit slower, but the extra work caused by the additional if-condition does not register compared to the slowness of serial communication. To that end, I cast a byte[4] over an int.

Namaan: To that end, I cast a byte[4] over an int.

int is 2 bytes on AVR.

I would think that if you want to discover the endianness of a float, you should place a specific float constant in your union, then test the actual bytes against known hex constants. This would also verify at runtime that the float format is in fact IEEE 754.

Gardner is right. Also, testing a float instead of an int protects you against multiple-endian systems, on which the endianess of the an int can be different from that of a float.

I personally wouldn't be sending floats in binary form from one computer to another, particularly if they are from different manufacturers or are using different library bases. There is no guarantee that the underlying format will be identical (even if you solve the endianness problem).

A couple of reasonable alternatives would be send a long (4 bytes) with suitable implied scaling (eg. divide by 1000) to get the floating representation back. Or you could send BCD-encoded data.

Thank you for your replies.
Gardner, Jean Suisse: Thank you for this excellent idea. I have implemented it and it works perfectly.
Nick Gammon: This is a nice idea. I have considered it for a time. But I would like to be more abstract and versatile. I should be able to transmit any value through the RS-485 connection. Plus, I already have a large range (1E3 to 1E-11) that make this option only valid if I transmit the long as well as a power of 10 in a byte for instance. To do so, I have to compute them on the board, which increases the work load.

Eventually, I went for:

FLOATS
– IEEE754 float network format, 32 bits
– Dynamic endianess detection at startup for floats, using floats

INTS
– 32 bit int network format
– Compile-time int size detection
– Dynamic int endianess detection using int.

This works fine with AVR, PIC32 and ARM. Communication has only been tested between an arduino-like board and a intel MAC running an Objective-C application designed and built with Xcode.

Thank you all for your help in this part of the project. I am deeply grateful.

This works fine with AVR, PIC32 and ARM.

Are any of those big-endian? ARM and MIPS (PIC32) have some chips that can be operated either big-endian or little-endian, but I don't think those have filtered down to the microcontroller-sized chips.