bbrownd
bbrownd

Reputation: 555

Reading double in C from binary unsigned char* with specified endianness

I have written a MariaDB/MySQL UDF in C for working with spatial data. I data is available to the function as an unsigned char*. The binary encoding of the data begins with a toggle bit signalling whether the stream is encoded little endian or big endian. Since this is the case I have used the following macros to read the unsigned 32 bit ints from the stream:

#define U32BIT_LE_DATA(ptr) (*(ptr)<<0) | (*(ptr + 1)<<8) | (*(ptr + 2)<<16) | (*(ptr + 3)<<24)
#define U32BIT_BE_DATA(ptr) (*(ptr + 3)<<0) | (*(ptr + 2)<<8) | (*(ptr + 1)<<16) | (*(ptr)<<24)
    
uint32_t var = U32BIT_LE_DATA(ptr); // Little endian encoding
uint32_t var = U32BIT_BE_DATA(ptr); // Big endian encoding

The stream also has doubles that I need to parse (64-bit (8 byte) double-precision data using the IEEE 754 double-precision format). I know I can do:

double var;
memcpy(&var, ptr, sizeof(double));

But this code is not very safe in regards to portability. I am aware that if I know my machines endiannes, then I can simply reverse the order of the bytes before calling memcpy. Nevertheless, is there a more reliable way to decode a double from or encode it to a 64bit IEEE 754 double-precision floating point using a specified endianness without needing to know the endianness (and system specific double layout) of the machine running the code?

Upvotes: 0

Views: 233

Answers (2)

0___________
0___________

Reputation: 67749

typedef union 
{
    double d;
    uint8_t b[sizeof(double)];
}u64;

inline double toDoubleLE(const uint8_t *arr, int endianess)
{
    u64 u;
    if (endianess)
    {
        for(size_t x = 0; x < sizeof(u); x++)
        {
            u.b[sizeof(u) - x - 1] = arr[x];
        }
    }
    else
    {
        for(size_t x = 0; x < sizeof(u); x++)
        {
            u.b[x] = arr[x];
        }
    }
    return u.d;
}

double fooLE(uint8_t *arr)
{
    return toDoubleLE(arr, 0);
}

double foobE(uint8_t *arr)
{
    return toDoubleLE(arr, 1);
}

compilers are "smart" and x86-64 will convert it to 2 machine code operations.

fooLE:
        movzx   eax, BYTE PTR [rdi]
        movq    xmm0, rax
        ret
foobE:
        mov     rax, QWORD PTR [rdi]
        bswap   rax
        movq    xmm0, rax
        ret

https://godbolt.org/z/ofpDGe

Upvotes: 1

Nevertheless, is there a more reliable way to decode a double from or encode it to a 64bit IEEE 754 double-precision floating point

On POSIX or Unix systems, connsider using XDR or ASN/1.

If the data is not too big, consider JSON (e.g. with Jansson) or perhaps YAML and decide to represent it in textual form. Read then carefully the documentation of fscanf and fprintf (e.g. related to %a format specifier).

See also the floating-point-gui.de

Upvotes: 0

Related Questions