2011-02-09 90 views
5

我正在嘗試爲在Win XP上運行的C++程序編寫一個byteswap例程。我與Visual Studio 2008編譯這是我想出來的:如何字節交換雙?

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; 
} 

,並測試一個功能:

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); 
     } 
    } 
} 

獲取這些數字不匹配:

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

這是在通讀後:
How do I convert between big-endian and little-endian values in C++?
Little Endian - Big Endian Problem
不能對雙用< <和>>,順便(除非我錯了?)

+1

你能否澄清究竟是在雷神之錘2發明的?當然不是將double和int64作爲同一個union的字段的想法嗎? – 2011-02-09 18:56:13

+0

對不起。我學會了Quake2的C,所以我喜歡假裝它是最偉大的:D我現在看到它(聯盟)。 – Darrell 2011-02-09 23:25:31

回答

4

嘗試3

好吧,發現有一個更好的辦法。另一種方式,你必須擔心你打包/解壓包裝的訂單。這樣,你不這樣做:

// int and float 
static void swap4(void *v) 
{ 
    char in[4], out[4]; 
    memcpy(in, v, 4); 
    out[0] = in[3]; 
    out[1] = in[2]; 
    out[2] = in[1]; 
    out[3] = in[0]; 
    memcpy(v, out, 4); 
} 

// double 
static void swap8(void *v) 
{ 
    char in[8], out[8]; 
    memcpy(in, v, 8); 
    out[0] = in[7]; 
    out[1] = in[6]; 
    out[2] = in[5]; 
    out[3] = in[4]; 
    out[4] = in[3]; 
    out[5] = in[2]; 
    out[6] = in[1]; 
    out[7] = in[0]; 
    memcpy(v, out, 8); 
} 

typedef struct 
{ 
    int theint; 
    float thefloat; 
    double thedouble; 
} mystruct; 


static void swap_mystruct(void *buf) 
{ 
    mystruct *ps = (mystruct *) buf; 
    swap4(&ps->theint); 
    swap4(&ps->thefloat); 
    swap8(&ps->thedouble); 
}  

發送:

char buf[sizeof (mystruct)]; 
    memcpy(buf, &s, sizeof (mystruct)); 
    swap_mystruct(buf); 

的Recv:

mystruct s; 
    swap_mystruct(buf); 
    memcpy(&s, buf, sizeof (mystruct)); 
6

雖然主存儲器中的double爲64位,在x86 CPU的雙精度寄存器80位寬。所以如果你的一個值存儲在一個寄存器中,而另一個在主存中進行往返,並且被截斷爲64位,這可以解釋你所看到的小差異。

也許你可以強制變量通過採取他們的地址(和打印出來,以防止編譯器優化出來)住在主內存中,但我不能肯定,這是保證工作。

+0

+1:我認爲這就是發生在這裏的情況:'a'被保存在一個寄存器中,當'c'在內存中時'a'被增加。 – mmmmmmmm 2011-02-09 19:36:23

3
b = byteswap(a); 

這是一個問題。交換字節後,該值不再是適當的雙精度值。當FPU規範化值時,將其重新存儲爲double會導致微妙的問題。您必須將其存儲回__int64(long long)。修改方法的返回類型。

0

嘗試2

好,知道了它的工作! Hans Passant是對的。他們讓我以「不再適當的雙倍」評論來思考。所以你不能將一個float轉換爲另一個float,因爲那樣它可能是不正確的格式,所以你必須將bytewap轉換成一個char數組並且不要返回。這是我使用的代碼:

int pack(int value, char *buf) 
{ 
    union temp { 
     int value; 
     char c[4]; 
    } in, out; 
    in.value = value; 
    out.c[0] = in.c[3]; 
    out.c[1] = in.c[2]; 
    out.c[2] = in.c[1]; 
    out.c[3] = in.c[0]; 
    memcpy(buf, out.c, 4); 
    return 4; 
} 

int pack(float value, char *buf) 
{ 
    union temp { 
     float value; 
     char c[4]; 
    } in, out; 
    in.value = value; 
    out.c[0] = in.c[3]; 
    out.c[1] = in.c[2]; 
    out.c[2] = in.c[1]; 
    out.c[3] = in.c[0]; 
    memcpy(buf, out.c, 4); 
    return 4; 
} 

int pack(double value, char *buf) 
{ 
    union temp { 
     double value; 
     char c[8]; 
    } in, out; 
    in.value = value; 
    out.c[0] = in.c[7]; 
    out.c[1] = in.c[6]; 
    out.c[2] = in.c[5]; 
    out.c[3] = in.c[4]; 
    out.c[4] = in.c[3]; 
    out.c[5] = in.c[2]; 
    out.c[6] = in.c[1]; 
    out.c[7] = in.c[0]; 
    memcpy(buf, out.c, 8); 
    return 8; 
} 

int unpack(char *buf, int *value) 
{ 
    union temp { 
     int value; 
     char c[4]; 
    } in, out; 
    memcpy(in.c, buf, 4); 
    out.c[0] = in.c[3]; 
    out.c[1] = in.c[2]; 
    out.c[2] = in.c[1]; 
    out.c[3] = in.c[0]; 
    memcpy(value, &out.value, 4); 
    return 4; 
} 

int unpack(char *buf, float *value) 
{ 
    union temp { 
     float value; 
     char c[4]; 
    } in, out; 
    memcpy(in.c, buf, 4); 
    out.c[0] = in.c[3]; 
    out.c[1] = in.c[2]; 
    out.c[2] = in.c[1]; 
    out.c[3] = in.c[0]; 
    memcpy(value, &out.value, 4); 
    return 4; 
} 

int unpack(char *buf, double *value) 
{ 
    union temp { 
     double value; 
     char c[8]; 
    } in, out; 
    memcpy(in.c, buf, 8); 
    out.c[0] = in.c[7]; 
    out.c[1] = in.c[6]; 
    out.c[2] = in.c[5]; 
    out.c[3] = in.c[4]; 
    out.c[4] = in.c[3]; 
    out.c[5] = in.c[2]; 
    out.c[6] = in.c[1]; 
    out.c[7] = in.c[0]; 
    memcpy(value, &out.value, 8); 
    return 8; 
} 

和一個簡單的測試功能:

typedef struct 
{ 
    int theint; 
    float thefloat; 
    double thedouble; 
} mystruct; 

void PackStruct() 
{ 
    char buf[sizeof (mystruct)]; 
    char *p; 
    p = buf; 

    mystruct foo, foo2; 
    foo.theint = 1; 
    foo.thefloat = 3.14f; 
    foo.thedouble = 400.5; 

    p += pack(foo.theint, p); 
    p += pack(foo.thefloat, p); 
    p += pack(foo.thedouble, p); 

    // Send or recv char array 

    p = buf; 
    p += unpack(p, &foo2.theint); 
    p += unpack(p, &foo2.thefloat); 
    p += unpack(p, &foo2.thedouble); 
}