Я использую "счетчик" или "цикл" как имя переменной. Современные IDE обычно делают завершение слова, более такие длинные имена переменной не так утомительны для использования. Кроме того, для именования переменной к ее функциональности проясняет программисту, который собирается поддержать код относительно того, каковы намерения были.
Предполагая, что вы используете основные компиляторы, значения с плавающей запятой в C и C ++ подчиняются стандарту IEEE и при записи в двоичной форме в файл могут быть восстановлены на любой другой платформе при условии, что вы напишете и читать, используя тот же порядок байтов. Итак, мое предложение: выберите предпочтительный порядок следования байтов и перед записью или после чтения проверьте, совпадает ли этот порядок байтов с порядком в текущей платформе; если нет, просто поменяйте байты местами.
Вы всегда можете преобразовать в формат IEEE-754 в фиксированном порядке байтов (либо немного endian или big endian). Для большинства машин это потребует либо вообще ничего, либо простой перестановки байтов для сериализации и десериализации. Машина, которая изначально не поддерживает IEEE-754, потребует написанного конвертера, но сделать это с помощью ldexp и frexp (стандартные функции библиотеки C) и перестановки битов не так уж сложно.
Это может дать вам хорошее начало - оно упаковывает значение с плавающей запятой в пару 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;
}
Что значит «переносимый»?
Для переносимости не забудьте сохранить числа в пределах, определенных в Стандарте: используйте одно число вне этих пределов, и все переносимость на ветер.
double planck_time = 5.39124E-44; /* second */
[...] 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 [...] [...]
Обратите внимание на , определенный реализацией во всех этих разделах.
Преобразование в представление ascii было бы самым простым, но если вам нужно иметь дело с колоссальным количеством чисел с плавающей запятой, тогда, конечно, вам следует использовать двоичный код. Но это может быть сложной проблемой, если вы заботитесь о переносимости. Числа с плавающей запятой на разных машинах представлены по-разному.
Если вы не хотите использовать стандартную библиотеку, тогда ваш двоичный сериализатор / десериализатор с плавающей запятой просто должен иметь «контракт» о том, где каждый бит попадает и что он представляет.
Вот забавный веб-сайт, чтобы помогите с этим: ссылка .
sprintf, fprintf? Вы не найдете более портативного, чем это.
Какой уровень переносимости вам нужен? Если файл должен быть прочитан на компьютере с той же ОС, на которой он был сгенерирован, вы должны использовать двоичный файл и просто сохранить и восстановить битовый шаблон. В противном случае, как сказал мальчиктео, ASCII - ваш друг.
В этой версии больше одного байта на одно значение с плавающей запятой чтобы указать порядок байтов. Но я думаю, что он все же не очень портативный.
#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;
}
fwrite (), fread ()? Вы, вероятно, захотите двоичный файл, и вы не можете упаковать байты более плотно, если вы не хотите пожертвовать точностью, которую вы бы сделали в программе, а затем в любом случае fwrite () fread (); float a; двойной б; a = (float) b; fwrite (& a, 1, sizeof (a), fp);
Если вы используете разные форматы с плавающей запятой, они могут не преобразовываться в прямом двоичном смысле, поэтому вам, возможно, придется разделить биты и выполнить математические вычисления, это к мощности, плюс это и т. д. IEEE 754 - ужасный стандарт для использования, но широко распространенный, поэтому он сведет к минимуму усилия.