Tre*_*vor 12 c floating-point serialization
如何在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;
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我从这个问题中找到了这些功能
编辑2:
序列化的目的是将数据发送到UDP套接字,并保证即使字节序不同,也可以在其他机器上反序列化.是否有任何其他"最佳实践"来执行此功能,因为我必须序列化整数,双精度数,浮点数和字符*?
R..*_*R.. 13
可移植方式:用于frexp序列化(转换为整数尾数和指数)和ldexp反序列化.
简单的方法:假设在2010年你关心的任何机器使用IEEE float,声明一个带float元素和uint32_t元素的联合,并使用整数序列化代码来序列化float.
二进制文件 - 仇恨方式:将所有内容序列化为文本,包括浮点数.使用"%a"printf格式说明符获得一个十六进制浮点数,它总是精确表示(假设您没有限制类似的精度"%.4a")并且不受舍入误差的影响.您可以使用strtod或任何scanf一系列功能阅读这些功能.
S.C*_*sen 12
我记得第一次看到我的示例中使用的演员在"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;
}
Run Code Online (Sandbox Code Playgroud)
我希望我能正确理解你的问题(和示例代码).如果这是有用的,请告诉我?
这包一个浮点值转换成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;
}
Run Code Online (Sandbox Code Playgroud)
无论本机表示如何,您都可以在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);
}
Run Code Online (Sandbox Code Playgroud)
在使用IEEE-754的机器(即常见情况)中,您需要做的就是获取数字fread().否则,自己解码字节(sign * 2^(exponent-127) * 1.mantissa).
注意:在本机double比IEEE double更精确的系统中进行序列化时,您可能会遇到低位的逐个错误.
希望这可以帮助.
更新后,您提到数据将使用 UDP 传输并询问最佳实践。我强烈建议以文本形式发送数据,甚至可能添加一些标记(XML)。在传输线上调试与字节序相关的错误是浪费每个人的时间
只是我对你问题的“最佳实践”部分的两分钱