如何字节换一个双?

时间:2011-02-09 18:51:40

标签: c++ visual-studio-2008 endianness

我正在尝试为在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
你不能使用&lt;&lt;和&gt;&gt;顺便说一下(除非我弄错了?)

5 个答案:

答案 0 :(得分:6)

虽然主存储器中的double是64位,但在x86 CPU上,双精度寄存器的宽度为80位。因此,如果您的一个值全部存储在寄存器中,但另一个值通过主存储器往返并被截断为64位,这可以解释您所看到的小差异。

也许您可以通过获取地址(并打印它,以防止编译器优化它)来强制变量存在于主存储器中,但我不确定这是否可以保证工作。

答案 1 :(得分: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));

答案 2 :(得分:3)

    b = byteswap(a);

这是一个问题。交换字节后,该值不再是正确的双倍。当FPU将值标准化时,将其存储回double会导致细微问题。你必须将它存储回__int64(很长)。修改方法的返回类型。

答案 3 :(得分:0)

尝试2

好的,让它运转起来!汉斯帕斯特是对的。他们让我思考“不再适当的双重”评论。所以你不能将float换行到另一个float,因为它可能是一个不正确的格式,所以你必须将byteswap转换为一个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);
}

答案 4 :(得分:0)

如何在任意数组,变量或任何其他内存块中替换字节

(例如int16_tuint16_tuint32_tfloatdouble等)

这是一种将效率从阵列的3个完整复制操作提高到阵列的1.5个完整复制操作的方法。另请参阅我留下的评论under your answer

/// \brief          Swap all the bytes in an array to convert from little-endian byte order 
///                 to big-endian byte order, or vice versa.
/// \note           Works for arrays of any size. Swaps the bytes **in place** in the array.
/// \param[in,out]  byte_array  The array in which to swap the bytes in-place.
/// \param[in]      len         The length (in bytes) of the array.
/// \return         None
void swap_bytes_in_array(uint8_t * byte_array, size_t len) {
    size_t i_left = 0; // index for left side of the array
    size_t i_right = len - 1; // index for right side of the array
    while (i_left < i_right) {
        // swap left and right bytes
        uint8_t left_copy = byte_array[i_left];
        byte_array[i_left] = byte_array[i_right];
        byte_array[i_right] = left_copy;
        i_left++;
        i_right--;
    }
}

用法:

double d;
// Swap the bytes in the double in place
swap_bytes_in_array((uint8_t*)(&d), sizeof(d));

uint64_t u64;
// swap the bytes in a uint64_t in place
swap_bytes_in_array((uint8_t*)(&u64), sizeof(u64));

但是请注意,@Hans Passant seems to be onto something here。尽管上面的方法在任何有符号或无符号 integer 类型上都可以完美地工作,并且对我来说似乎也可以在floatdouble上使用,但似乎在{{1} }。我认为这是因为当我将交换后的long double存储回long double变量中时,如果确定它不再是有效的long double表示形式,则某些内容会自动更改交换字节或其他内容。我不太确定。

在许多64位系统上,long double是16字节,因此解决方案是将long double的交换版本保留在16字节数组中,而不要尝试使用或强制转换将其从long double 16字节数组返回到long double,直到任一A)它已发送到接收器(系统的字节序相反,因此现在状态良好)和/或B)再次交换字节,因此它又是有效的uint8_t

请记住以上几点,以防您也遇到long doublefloat类型的问题,就像我看到的只有double类型一样。