2011-02-09 5 views
5

私はWin XPで実行されているC++プログラムのためのバイトワイプルーチンを作成しようとしています。ダブルバイトする方法は?

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

とテストへの関数::

 
-76.789999999988126 -76.790000000017230 0.000000000029104 
-30.499999999987718 -30.499999999994994 0.000000000007276 
 41.790000000014508  41.790000000029060 -0.000000000014552 
 90.330000000023560  90.330000000052664 -0.000000000029104 
を:これらの数字が一致しないガイド

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

私はこれは私が作ってみたものですのVisual Studio 2008でコンパイルしています

これは読んだ後です:
How do I convert between big-endian and little-endian values in C++?
(私は間違えてる場合を除き?)あなたは方法によって、二重に< <と>>を使用することはできません

+1

正確クエイク2に発明された何を明確にもらえますか?確かに、同じ組合のフィールドとしてdoubleとint64を持つ考えはありませんか? –

+0

申し訳ありません..私はQuake2のCを学んだので、私はそれが一番だとふりをするのが好きです:D私はそれを今どこにでも見ています。 – Darrell

答えて

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ある210
ビット幅。したがって、あなたの値の1つがレジスタ全体に格納されていて、もう一方がメインメモリを往復して64ビットに切り捨てられている場合、これはあなたが見ている小さな違いを説明することができます。

変数を主メモリに強制的に置くことで、アドレスを取得してコンパイラが最適化できないようにすることができますが、これが動作することが保証されているとは確信していません。

+0

+1:ここで起こるのは、 'a'はレジスタに保持され、 'c'がメモリにある間にインクリメントされるということです。 – mmmmmmmm

3
b = byteswap(a); 

これは問題です。バイトを交換した後、値はもはや正しいdoubleではなくなります。倍精度に戻すと、FPUが値を正規化するときに微妙な問題が発生します。 __int64(long long)に戻す必要があります。メソッドの戻り値の型を変更します。

+0

それは実際に理にかなっています!さて、これを調べて、あなたに戻ってきます。 – Darrell

0

さて2

を試してみて、それが働きました!ハンス・パサントが正しかった。彼らは私に「もはや適切な倍の」コメントを考えさせた。したがって、浮動小数点数を別の浮動小数点数にバイト・ワップすることはできません。これは、浮動小数点数が不適切な形式になる可能性があるためです。したがって、文字配列にバイト・ワープして戻す必要があります。

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); 
} 
関連する問題