Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to byteswap a double?

I'm trying to write a byteswap routine for a C++ program running on Win XP. I'm compiling with Visual Studio 2008. This is what I've come up with:

int byteswap(int v) // This is good
{
    return _byteswap_ulong(v);
}

double byteswap(double v) // This doesn't work for some values
{
    union { // This trick is first used in Quake2 source I believe :D
        __int64 i;
        double  d;
    } conv;
    conv.d = v;
    conv.i = _byteswap_uint64(conv.i);
    return conv.d;
}

And a function to test:

void testit() {
    double  a, b, c;
    CString str;

    for (a = -100; a < 100; a += 0.01) {
        b = byteswap(a);
        c = byteswap(b);
        if (a != c) {
            str.Format("%15.15f %15.15f %15.15f", a, c, a - c);
        }
    }
}

Getting these numbers not matching:

-76.789999999988126 -76.790000000017230 0.000000000029104  
-30.499999999987718 -30.499999999994994 0.000000000007276  
 41.790000000014508  41.790000000029060 -0.000000000014552  
 90.330000000023560  90.330000000052664 -0.000000000029104

This is after having read through:
How do I convert between big-endian and little-endian values in C++?
Little Endian - Big Endian Problem
You can't use << and >> on double, by the way (unless I'm mistaken?)

like image 530
Darrell Avatar asked Feb 09 '11 18:02

Darrell


2 Answers

Although a double in main memory is 64 bits, on x86 CPUs double-precision registers are 80 bits wide. So if one of your values is stored in a register throughout, but the other makes a round-trip through main memory and is truncated to 64 bits, this could explain the small differences you're seeing.

Maybe you can force variables to live in main memory by taking their address (and printing it, to prevent the compiler from optimizing it out), but I'm not certain that this is guaranteed to work.

like image 186
Thomas Avatar answered Sep 22 '22 22:09

Thomas


    b = byteswap(a);

That's a problem. After swapping the bytes, the value is no longer a proper double. Storing it back to a double is going to cause subtle problems when the FPU normalizes the value. You have to store it back into an __int64 (long long). Modify the return type of the method.

like image 26
Hans Passant Avatar answered Sep 21 '22 22:09

Hans Passant