C - Сериализация чисел с плавающей точкой (плавания, удваивается),

Я использую "счетчик" или "цикл" как имя переменной. Современные IDE обычно делают завершение слова, более такие длинные имена переменной не так утомительны для использования. Кроме того, для именования переменной к ее функциональности проясняет программисту, который собирается поддержать код относительно того, каковы намерения были.

13
задан Lorenzo Donati supports Monica 21 September 2013 в 15:24
поделиться

9 ответов

Предполагая, что вы используете основные компиляторы, значения с плавающей запятой в C и C ++ подчиняются стандарту IEEE и при записи в двоичной форме в файл могут быть восстановлены на любой другой платформе при условии, что вы напишете и читать, используя тот же порядок байтов. Итак, мое предложение: выберите предпочтительный порядок следования байтов и перед записью или после чтения проверьте, совпадает ли этот порядок байтов с порядком в текущей платформе; если нет, просто поменяйте байты местами.

12
ответ дан 1 December 2019 в 22:57
поделиться

Вы всегда можете преобразовать в формат IEEE-754 в фиксированном порядке байтов (либо немного endian или big endian). Для большинства машин это потребует либо вообще ничего, либо простой перестановки байтов для сериализации и десериализации. Машина, которая изначально не поддерживает IEEE-754, потребует написанного конвертера, но сделать это с помощью ldexp и frexp (стандартные функции библиотеки C) и перестановки битов не так уж сложно.

3
ответ дан 1 December 2019 в 22:57
поделиться

Это может дать вам хорошее начало - оно упаковывает значение с плавающей запятой в пару int и long long , которые затем можно сериализовать в обычным способом.

#define FRAC_MAX 9223372036854775807LL /* 2**63 - 1 */

struct dbl_packed
{
    int exp;
    long long frac;
};

void pack(double x, struct dbl_packed *r)
{
    double xf = fabs(frexp(x, &r->exp)) - 0.5;

    if (xf < 0.0)
    {
        r->frac = 0;
        return;
    }

    r->frac = 1 + (long long)(xf * 2.0 * (FRAC_MAX - 1));

    if (x < 0.0)
        r->frac = -r->frac;
}

double unpack(const struct dbl_packed *p)
{
    double xf, x;

    if (p->frac == 0)
        return 0.0;

    xf = ((double)(llabs(p->frac) - 1) / (FRAC_MAX - 1)) / 2.0;

    x = ldexp(xf + 0.5, p->exp);

    if (p->frac < 0)
        x = -x;

    return x;
}
3
ответ дан 1 December 2019 в 22:57
поделиться

Что значит «переносимый»?

Для переносимости не забудьте сохранить числа в пределах, определенных в Стандарте: используйте одно число вне этих пределов, и все переносимость на ветер.

double planck_time = 5.39124E-44; /* second */

5.2.4.2.2 Характеристики плавающих типов

[...]
10   The values given in the following list shall be replaced by constant
     expressions with implementation-defined values [...]
11   The values given in the following list shall be replaced by constant
     expressions with implementation-defined values [...]
12   The values given in the following list shall be replaced by constant
     expressions with implementation-defined (positive) values [...]
[...]

Обратите внимание на , определенный реализацией во всех этих разделах.

2
ответ дан 1 December 2019 в 22:57
поделиться

Преобразование в представление ascii было бы самым простым, но если вам нужно иметь дело с колоссальным количеством чисел с плавающей запятой, тогда, конечно, вам следует использовать двоичный код. Но это может быть сложной проблемой, если вы заботитесь о переносимости. Числа с плавающей запятой на разных машинах представлены по-разному.

Если вы не хотите использовать стандартную библиотеку, тогда ваш двоичный сериализатор / десериализатор с плавающей запятой просто должен иметь «контракт» о том, где каждый бит попадает и что он представляет.

Вот забавный веб-сайт, чтобы помогите с этим: ссылка .

1
ответ дан 1 December 2019 в 22:57
поделиться

sprintf, fprintf? Вы не найдете более портативного, чем это.

0
ответ дан 1 December 2019 в 22:57
поделиться

Какой уровень переносимости вам нужен? Если файл должен быть прочитан на компьютере с той же ОС, на которой он был сгенерирован, вы должны использовать двоичный файл и просто сохранить и восстановить битовый шаблон. В противном случае, как сказал мальчиктео, ASCII - ваш друг.

0
ответ дан 1 December 2019 в 22:57
поделиться

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

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

#define LITEND      'L'
#define BIGEND      'B'

typedef short               INT16;
typedef int                 INT32;
typedef double              vec1_t;

 typedef struct {
    FILE            *fp;
} WFILE, RFILE;

#define w_byte(c, p)    putc((c), (p)->fp)
#define r_byte(p)       getc((p)->fp)

static void w_vec1(vec1_t v1_Val, WFILE *p)
{
    INT32   i;
    char    *pc_Val;

    pc_Val = (char *)&v1_Val;

    w_byte(LITEND, p);
    for (i = 0; i<sizeof(vec1_t); i++)
    {
        w_byte(pc_Val[i], p);
    }
}


static vec1_t r_vec1(RFILE *p)
{
    INT32   i;
    vec1_t  v1_Val;
    char    c_Type,
            *pc_Val;

    pc_Val = (char *)&v1_Val;

    c_Type = r_byte(p);
    if (c_Type==LITEND)
    {
        for (i = 0; i<sizeof(vec1_t); i++)
        {
            pc_Val[i] = r_byte(p);
        }
    }
    return v1_Val;
}

int main(void)
{
    WFILE   x_FileW,
            *px_FileW = &x_FileW;
    RFILE   x_FileR,
            *px_FileR = &x_FileR;

    vec1_t  v1_Val;
    INT32   l_Val;
    char    *pc_Val = (char *)&v1_Val;
    INT32   i;

    px_FileW->fp = fopen("test.bin", "w");
    v1_Val = 1234567890.0987654321;
    printf("v1_Val before write = %.20f \n", v1_Val);
    w_vec1(v1_Val, px_FileW);
    fclose(px_FileW->fp);

    px_FileR->fp = fopen("test.bin", "r");
    v1_Val = r_vec1(px_FileR);
    printf("v1_Val after read = %.20f \n", v1_Val);
    fclose(px_FileR->fp);
    return 0;
}
0
ответ дан 1 December 2019 в 22:57
поделиться

fwrite (), fread ()? Вы, вероятно, захотите двоичный файл, и вы не можете упаковать байты более плотно, если вы не хотите пожертвовать точностью, которую вы бы сделали в программе, а затем в любом случае fwrite () fread (); float a; двойной б; a = (float) b; fwrite (& a, 1, sizeof (a), fp);

Если вы используете разные форматы с плавающей запятой, они могут не преобразовываться в прямом двоичном смысле, поэтому вам, возможно, придется разделить биты и выполнить математические вычисления, это к мощности, плюс это и т. д. IEEE 754 - ужасный стандарт для использования, но широко распространенный, поэтому он сведет к минимуму усилия.

-1
ответ дан 1 December 2019 в 22:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: