如何在C中序列化双打和浮点数?
我有以下代码来序列化short,int和chars.
unsigned char * serialize_char(unsigned char *buffer, char value) { buffer[0] = value; return buffer + 1; } unsigned char * serialize_int(unsigned char *buffer, int value) { buffer[0] = value >> 24; buffer[1] = value >> 16; buffer[2] = value >> 8; buffer[3] = value; return buffer + 4; } unsigned char * serialize_short(unsigned char *buffer, short value) { buffer[0] = value >> 8; buffer[1] = value; return buffer + 2; }
编辑:
我从这个问题中找到了这些功能
编辑2:
序列化的目的是将数据发送到UDP套接字,并保证即使字节序不同,也可以在其他机器上反序列化.是否有任何其他"最佳实践"来执行此功能,因为我必须序列化整数,双精度数,浮点数和字符*?
可移植方式:用于frexp
序列化(转换为整数尾数和指数)和ldexp
反序列化.
简单的方法:假设在2010年你关心的任何机器使用IEEE float,声明一个带float
元素和uint32_t
元素的联合,并使用整数序列化代码来序列化float.
二进制文件 - 仇恨方式:将所有内容序列化为文本,包括浮点数.使用"%a"
printf格式说明符获得一个十六进制浮点数,它总是精确表示(假设您没有限制类似的精度"%.4a"
)并且不受舍入误差的影响.您可以使用strtod
或任何scanf
一系列功能阅读这些功能.
我记得第一次看到我的示例中使用的演员在"rsqrt"例程的古老的Quake源代码中,包含我当时看到的最酷的评论(谷歌它,你会喜欢它)
unsigned char * serialize_float(unsigned char *buffer, float value) { unsigned int ivalue = *((unsigned int*)&value); // warning assumes 32-bit "unsigned int" buffer[0] = ivalue >> 24; buffer[1] = ivalue >> 16; buffer[2] = ivalue >> 8; buffer[3] = ivalue; return buffer + 4; }
我希望我能正确理解你的问题(和示例代码).如果这是有用的,请告诉我?
这包一个浮点值转换成int
与long long
对,然后你就可以与其他功能连载.该unpack()
函数用于反序列化.
这对数字分别代表数字的指数和小数部分.
#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; }
无论本机表示如何,您都可以在IEEE-754中进行便携式序列化:
int fwriteieee754(double x, FILE * fp, int bigendian) { int shift; unsigned long sign, exp, hibits, hilong, lowlong; double fnorm, significand; int expbits = 11; int significandbits = 52; /* zero (can't handle signed zero) */ if(x == 0) { hilong = 0; lowlong = 0; goto writedata; } /* infinity */ if(x > DBL_MAX) { hilong = 1024 + ((1 << (expbits - 1)) - 1); hilong <<= (31 - expbits); lowlong = 0; goto writedata; } /* -infinity */ if(x < -DBL_MAX) { hilong = 1024 + ((1 << (expbits - 1)) - 1); hilong <<= (31 - expbits); hilong |= (1 << 31); lowlong = 0; goto writedata; } /* NaN - dodgy because many compilers optimise out this test * isnan() is C99, POSIX.1 only, use it if you will. */ if(x != x) { hilong = 1024 + ((1 << (expbits - 1)) - 1); hilong <<= (31 - expbits); lowlong = 1234; goto writedata; } /* get the sign */ if(x < 0) { sign = 1; fnorm = -x; } else { sign = 0; fnorm = x; } /* get the normalized form of f and track the exponent */ shift = 0; while(fnorm >= 2.0) { fnorm /= 2.0; shift++; } while(fnorm < 1.0) { fnorm *= 2.0; shift--; } /* check for denormalized numbers */ if(shift < -1022) { while(shift < -1022) { fnorm /= 2.0; shift++; } shift = -1023; } else { /* take the significant bit off mantissa */ fnorm = fnorm - 1.0; } /* calculate the integer form of the significand */ /* hold it in a double for now */ significand = fnorm * ((1LL << significandbits) + 0.5f); /* get the biased exponent */ exp = shift + ((1 << (expbits - 1)) - 1); /* shift + bias */ /* put the data into two longs */ hibits = (long)(significand / 4294967296); /* 0x100000000 */ hilong = (sign << 31) | (exp << (31 - expbits)) | hibits; lowlong = (unsigned long)(significand - hibits * 4294967296); writedata: /* write the bytes out to the stream */ if(bigendian) { fputc((hilong >> 24) & 0xFF, fp); fputc((hilong >> 16) & 0xFF, fp); fputc((hilong >> 8) & 0xFF, fp); fputc(hilong & 0xFF, fp); fputc((lowlong >> 24) & 0xFF, fp); fputc((lowlong >> 16) & 0xFF, fp); fputc((lowlong >> 8) & 0xFF, fp); fputc(lowlong & 0xFF, fp); } else { fputc(lowlong & 0xFF, fp); fputc((lowlong >> 8) & 0xFF, fp); fputc((lowlong >> 16) & 0xFF, fp); fputc((lowlong >> 24) & 0xFF, fp); fputc(hilong & 0xFF, fp); fputc((hilong >> 8) & 0xFF, fp); fputc((hilong >> 16) & 0xFF, fp); fputc((hilong >> 24) & 0xFF, fp); } return ferror(fp); }
在使用IEEE-754的机器(即常见情况)中,您需要做的就是获取数字fread()
.否则,自己解码字节(sign * 2^(exponent-127) * 1.mantissa)
.
注意:在本机double比IEEE double更精确的系统中进行序列化时,您可能会遇到低位的逐个错误.
希望这可以帮助.