Как byteswap двойной? - PullRequest
       2

Как byteswap двойной?

6 голосов
/ 09 февраля 2011

Я пытаюсь написать процедуру смены байтов для программы на C ++, работающей на Win XP. Я компилирую с 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

Это после прочтения:
Как преобразовать значения с прямым и младшим порядком в C ++?
Little Endian - проблема Big Endian
Кстати, нельзя использовать << и >> на двойном (разве я не ошибаюсь?)

Ответы [ 4 ]

6 голосов
/ 09 февраля 2011

Хотя double в основной памяти составляет 64 бита, в процессорах x86 регистры двойной точности имеют ширину 80 бит.Таким образом, если одно из ваших значений хранится в регистре, но другое выполняет обход по основной памяти и усекается до 64 бит, это может объяснить небольшие различия, которые вы видите.

Возможно, выможет заставить переменные жить в основной памяти, беря их адрес (и печатая его, чтобы компилятор не оптимизировал его), но я не уверен, что это гарантированно сработает.

4 голосов
/ 21 февраля 2011

Попробуйте 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));
3 голосов
/ 10 февраля 2011
    b = byteswap(a);

Это проблема. После замены байтов значение больше не является правильным двойным числом. Возвращение его в двойное число вызовет тонкие проблемы, когда FPU нормализует значение. Вы должны сохранить его обратно в __int64 (long long). Измените тип возвращаемого значения метода.

0 голосов
/ 15 февраля 2011

Попробуйте 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);
}
...