views:

204

answers:

3

Im trying to change from big endian to little endian on a double. One way to go is to use

double val, tmp = 5.55;

((unsigned int *)&val)[0] = ntohl(((unsigned int *)&tmp)[1]);
((unsigned int *)&val)[1] = ntohl(((unsigned int *)&tmp)[0]);

But then I get a warning: "dereferencing type-punned pointer will break strict-aliasing rules" and I dont want to turn this warning off.

Another way to go is:

#define ntohll(x) ( ( (uint64_t)(ntohl( (uint32_t)((x << 32) >> 32) )) << 32) | ntohl( ((uint32_t)(x >> 32)) ) ) 

val = (double)bswap_64(unsigned long long(tmp)); //or
val = (double)ntohll(unsigned long long(tmp));

But then a lose the decimals. Anyone know a good way to swap the bits on a double without using a for loop?

A: 

Can't you just swap them around?

inline unsigned long long EndianChange( double d )
{
    char ch[8];
    memcpy( ch, &d, 8 );  // Note this will be optimised out completely by pretty much every compiler.
    ch[0] ^= ch[7] ^= ch[0] ^= ch[7]; 
    ch[1] ^= ch[6] ^= ch[1] ^= ch[6];
    ch[2] ^= ch[5] ^= ch[2] ^= ch[5];
    ch[3] ^= ch[4] ^= ch[3] ^= ch[4];

    unsigned long long dRet;
    memcpy( &dRet, ch, 8 ); // Again this will get optimised out.
    return dRet;
};

Edit: As pointed out the byte swapped double "can" get loaded into a register so that getting it back out of that register can mean the value is no longer valid so store it in a long long, 64-bit to avoid this problem.

Thats all there is to a byte swap. I'm not sure what you are trying to do but every big endian platform I've ever used uses the same encoding as little-endian just the byte order reversed. The above code will reverse the byte order for you. Pretty much any compiler will simply do the byte swap and then return the byte swapped variable and get rid of the memcpys. This is a good way to deal with aliasing issues.

Goz
This solution definitely doesn't work - an endian-changed double is likely to no longer be a valid FP value, and will cause undefined behaviour in the FPU. See @Max Lybbert's link.
stusmith
Good point ... fixed.
Goz
+6  A: 

I'd probably try something like this:

template <typename T>
void swap_endian(T& pX)
{
    // should static assert that T is a POD

    char& raw = reinterpret_cast<char&>(pX);
    std::reverse(&raw, &raw + sizeof(T));
}

Short and sweet (and relatively untested). The compiler will make all the necessary optimizations. The above is well-defined for any POD type, and doesn't rely on any implementation details.

A copy version, for when you don't want to modify the argument:

template <typename T>
T swap_endian_copy(T pX)
{
    swap_endian(pX);
    return pX;
}
GMan
+2  A: 

There are some important pitfalls to pay attention to when dealing with the binary representation of floats or doubles.

Max Lybbert