Проблемы с преобразованием байтового массива в float / long в C - PullRequest
1 голос
/ 30 августа 2010

Long:

  char long_num[8];
  for(i=0; i<8; i++)
    long_num[i] = data[pos++];

  memcpy(&res, long_num, 8);

Значения в long_num следующие:

127 -1 -1 -1 -1 -1 -1 -1

res должно быть максимальным значением со знаком long, но вместо этого равно -129.

РЕДАКТИРОВАТЬ: Об этом позаботятся.Это было результатом проблем со связью: для человека, предоставляющего data, long составляет восемь байтов;для моего C это четыре.

Float:

  float *res;
  /* ... */
  char float_num[4];
  for(i=0; i<4; i++)
    float_num[i] = data[pos++];

  res = (float *)float_num;

Это ноль.Значения массива:

62 -1 24 50

Я также пытался memcpy(), но это также дает ноль.Что я делаю не так?


Моя система: Linux 2.6.31-16-generic i686 GNU/Linux

Ответы [ 4 ]

3 голосов
/ 30 августа 2010

Вы запускаете код в системе little-endian . Обратный порядок байтов в массиве и попробуйте снова:

signed char long_num[] = {-1, -1, -1, -1, -1, -1, -1, 127};
// ...
2 голосов
/ 30 августа 2010

Это два совершенно не связанных между собой вопроса.

В первом случае ваш компьютер имеет младший порядок.Знаковый бит установлен в long, который вы собираете вместе, поэтому результат будет отрицательным.Он близок к нулю, потому что установлено много «старших значащих битов».

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

1 голос
/ 30 августа 2010

Учитывая этот код:

#include <stdio.h>
#include <string.h>

int main(void)
{
    {
        long res;
        char long_num[8] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
        memcpy(&res, long_num, 8);
        printf("%ld = 0x%lX\n", res, res);
    }
    {
        float res;

        char float_num[4] = { 62, 0xFF, 24, 50 };
        memcpy(&res, float_num, 4);
        printf("%f = %19.14e\n", res, res);

    }
    return 0;
}

Компиляция в 64-битном режиме на MacOS X 10.6.4 с GCC 4.5.1 дает:

-129 = 0xFFFFFFFFFFFFFF7F
0.000000 = 8.90559981314709e-09

Это немного корректно-endian Intel машина (ну, значение 'long' правильное).

То, что вы пытаетесь сделать, немного необычно - не рекомендуется.Он не переносим, ​​не в последнюю очередь из-за проблем с порядком байтов.

Ранее я писал некоторый связанный код на машине SPARC (которая является машиной с прямым порядком байтов):

union u_double
{
    double  dbl;
    char    data[sizeof(double)];
};

union u_float
{
    float   flt;
    char    data[sizeof(float)];
};

static void dump_float(union u_float f)
{
    int exp;
    long mant;

    printf("32-bit float: sign: %d, ", (f.data[0] & 0x80) >> 7);
    exp = ((f.data[0] & 0x7F) << 1) | ((f.data[1] & 0x80) >> 7);
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 127);
    mant = ((((f.data[1] & 0x7F) << 8) | (f.data[2] & 0xFF)) << 8) | (f.data[3] & 0xFF);
    printf("mant: %16ld (0x%06lX)\n", mant, mant);
}

static void dump_double(union u_double d)
{
    int exp;
    long long mant;

    printf("64-bit float: sign: %d, ", (d.data[0] & 0x80) >> 7);
    exp = ((d.data[0] & 0x7F) << 4) | ((d.data[1] & 0xF0) >> 4);
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 1023);
    mant = ((((d.data[1] & 0x0F) << 8) | (d.data[2] & 0xFF)) << 8) | (d.data[3] & 0xFF);
    mant = (mant << 32) | ((((((d.data[4] & 0xFF) << 8) | (d.data[5] & 0xFF)) << 8) | (d.data[6] & 0xFF)) << 8) | (d.data[7] & 0xFF);
    printf("mant: %16lld (0x%013llX)\n", mant, mant);
}

static void print_value(double v)
{
    union u_double d;
    union u_float  f;

    f.flt = v;
    d.dbl = v;

    printf("SPARC: float/double of %g\n", v);
    image_print(stdout, 0, f.data, sizeof(f.data));
    image_print(stdout, 0, d.data, sizeof(d.data));
    dump_float(f);
    dump_double(d);
}


int main(void)
{
    print_value(+1.0);
    print_value(+2.0);
    print_value(+3.0);
    print_value( 0.0);
    print_value(-3.0);
    print_value(+3.1415926535897932);
    print_value(+1e126);
    return(0);
}

Это то, что я получил на этой платформе.Обратите внимание, что в мантиссе есть неявный бит «1», поэтому значение «3» имеет только один бит, потому что подразумевается другой 1-бит.

SPARC: float/double of 1
0x0000: 3F 80 00 00                                       ?...
0x0000: 3F F0 00 00 00 00 00 00                           ?.......
32-bit float: sign: 0, expt:  127 (unbiassed     0), mant:                0 (0x000000)
64-bit float: sign: 0, expt: 1023 (unbiassed     0), mant:                0 (0x0000000000000)
SPARC: float/double of 2
0x0000: 40 00 00 00                                       @...
0x0000: 40 00 00 00 00 00 00 00                           @.......
32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:                0 (0x000000)
64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant:                0 (0x0000000000000)
SPARC: float/double of 3
0x0000: 40 40 00 00                                       @@..
0x0000: 40 08 00 00 00 00 00 00                           @.......
32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:          4194304 (0x400000)
64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant: 2251799813685248 (0x8000000000000)
SPARC: float/double of 0
0x0000: 00 00 00 00                                       ....
0x0000: 00 00 00 00 00 00 00 00                           ........
32-bit float: sign: 0, expt:    0 (unbiassed  -127), mant:                0 (0x000000)
64-bit float: sign: 0, expt:    0 (unbiassed -1023), mant:                0 (0x0000000000000)
SPARC: float/double of -3
0x0000: C0 40 00 00                                       .@..
0x0000: C0 08 00 00 00 00 00 00                           ........
32-bit float: sign: 1, expt:  128 (unbiassed     1), mant:          4194304 (0x400000)
64-bit float: sign: 1, expt: 1024 (unbiassed     1), mant: 2251799813685248 (0x8000000000000)
SPARC: float/double of 3.14159
0x0000: 40 49 0F DB                                       @I..
0x0000: 40 09 21 FB 54 44 2D 18                           @.!.TD-.
32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:          4788187 (0x490FDB)
64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant: 2570638124657944 (0x921FB54442D18)
SPARC: float/double of 1e+126
0x0000: 7F 80 00 00                                       ....
0x0000: 5A 17 A2 EC C4 14 A0 3F                           Z......?
32-bit float: sign: 0, expt:  255 (unbiassed   128), mant:                0 (0x000000)
64-bit float: sign: 0, expt: 1441 (unbiassed   418), mant:      -1005281217 (0xFFFFFFFFC414A03F)

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

0 голосов
/ 30 августа 2010

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

  • Один стандартный метод состоит в определении канонического порядка для байтов - и канонического размера для типов.Например, это часто называется «порядком байтов в сети» при работе с адресами IPv4.Это частично определяет порядковый номер данных;речь также идет об определении того, что значение отправляется как 4-байтовое значение, а не как 8-байтовое значение - или наоборот.

  • Другой метод основан на ASN.1 -который кодирует данные с типом, длиной и значением (кодировка TLV).Каждый бит данных отправляется с информацией, которая идентифицирует, что именно отправляется.

  • Протокол DRDA, используемый СУБД IBM DB2, имеет другую политику - «получатель делает правильно».Отправитель определяет, какой машиной он является, когда начинается сеанс, а затем отправляет данные в своем наиболее удобном формате.Получатель отвечает за исправление отправленного.(Это относится как к серверу БД, так и к клиенту БД; клиент отправляет в своей предпочтительной нотации, а сервер исправляет то, что он получает, тогда как сервер отправляет в своей предпочтительной нотации, а клиент исправляет то, что он получает.)

  • Другим чрезвычайно эффективным способом решения проблем является использование текстового протокола.Данные передаются в виде текстовой версии данных с четким механизмом идентификации различных полей.Это намного проще в отладке, чем различные механизмы двоичного кодирования, потому что вы можете сбросить данные и посмотреть, что происходит.Он не обязательно намного менее эффективен, чем двоичный протокол, особенно если вы обычно отправляете 8-байтовые целые числа, которые на самом деле содержат однозначные целочисленные значения.

...