C中的数据序列化?
我有一个我想写入文件的结构:C中的数据序列化?,c,serialization,floating-point,C,Serialization,Floating Point,我有一个我想写入文件的结构: typedef struct { char* egg; unsigned long sausage; long bacon; double spam; } order; 此文件必须是二进制文件,并且任何具有 C99编译器 我研究了各种方法,如ASN.1、XDR、XML、, ProtocolBuffers和许多其他产品,但它们都不符合我的要求: 小的 简单的 用C写的 然后我决定制定自己的数据协议。我能处理这个问题 整数类型的以下
typedef struct
{
char* egg;
unsigned long sausage;
long bacon;
double spam;
} order;
此文件必须是二进制文件,并且任何具有
C99编译器
我研究了各种方法,如ASN.1、XDR、XML、,
ProtocolBuffers和许多其他产品,但它们都不符合我的要求:
- 小的
- 简单的
- 用C写的
- 未签名
- 补签
- 以2的补码签名
- 符号和大小
float
和double
?标准
表示按位运算符(至少&
,|
,
和code>)用于
integer只输入类型,这让我没有希望。我唯一能做的就是
我认为:
int sign;
int exponent;
unsigned long mantissa;
order my_order;
sign = read_sign();
exponent = read_exponent();
mantissa = read_mantissa();
my_order.spam = sign * mantissa * pow(10, exponent);
但这似乎不是很有效。我也找不到合适的人选
double
和float
的表示说明。应该怎么做
在此之前继续?如果您使用的是IEEE-754,为什么不将
浮点
或双精度
作为无符号短
或无符号长
访问,并将浮点数据保存为一系列字节,然后重新转换“专用”unsigned short
或unsigned long
返回到变速箱另一侧的float
或double
。。。位数据将被保留,因此在传输后,您应该使用相同的浮点数。如果您使用C99,您可以使用%a以可携式十六进制输出实数。如果您希望使用浮点尽可能可携式,您可以使用frexp和ldexp:
void WriteFloat (float number)
{
int exponent;
unsigned long mantissa;
mantissa = (unsigned int) (INT_MAX * frexp(number, &exponent);
WriteInt (exponent);
WriteUnsigned (mantissa);
}
float ReadFloat ()
{
int exponent = ReadInt();
unsigned long mantissa = ReadUnsigned();
float value = (float)mantissa / INT_MAX;
return ldexp (value, exponent);
}
这背后的想法是,ldexp、frexp和INT_MAX是标准的C。同时,无符号长的精度通常至少与尾数的宽度一样高(不保证,但这是一个有效的假设,我不知道这里有什么不同的体系结构)
因此,转换工作无精度损失。INT_MAX的除法/乘法在转换过程中可能会失去一点精度,但这是一种折衷方法。C标准没有定义浮点类型的表示形式。您最好将它们转换为IEEE-754格式并以这种方式存储。我可以帮你
请注意,C标准也没有为整数指定格式。虽然你可能会遇到的大多数计算机都会使用正常的补码表示法,只需要关注尾数,但它们也可能使用补码或符号大小表示法,有符号整数和无符号整数都可能包含对值没有影响的填充位。这个答案使用了Nils Pipenbrinck的方法,但我已经更改了一些细节,我认为这些细节有助于确保C99真正的可移植性。此解决方案存在于一个虚构的环境中,encode\u int64
和encode\u int32
等已经存在
#include <stdint.h>
#include <math.h>
#define PORTABLE_INTLEAST64_MAX ((int_least64_t)9223372036854775807) /* 2^63-1*/
/* NOTE: +-inf and nan not handled. quickest solution
* is to encode 0 for !isfinite(val) */
void encode_double(struct encoder *rec, double val) {
int exp = 0;
double norm = frexp(val, &exp);
int_least64_t scale = norm*PORTABLE_INTLEAST64_MAX;
encode_int64(rec, scale);
encode_int32(rec, exp);
}
void decode_double(struct encoder *rec, double *val) {
int_least64_t scale = 0;
int_least32_t exp = 0;
decode_int64(rec, &scale);
decode_int32(rec, &exp);
*val = ldexp((double)scale/PORTABLE_INTLEAST64_MAX, exp);
}
#包括
#包括
#定义可移植的最小值((最小值)9223372036854775807)/*2^63-1*/
/*注意:+-inf和nan未处理。最快解决方案
*是将0编码为!isfinite(val)*/
void encode_double(结构编码器*rec,double val){
int exp=0;
双范数=frexp(val和exp);
最小内标度=标准*最大便携式内标度64;
编码_int64(rec,scale);
编码_int32(rec,exp);
}
void decode_double(结构编码器*rec,double*val){
内部最小64度刻度=0;
int_least32_t exp=0;
解码int64(rec和scale);
解码int32(rec和exp);
*val=ldexp((双)刻度/便携式仪器最大值64,exp);
}
这仍然不是真正的解决方案,inf
和nan
无法编码。还要注意,双进位符号位的两个部分
int\u least64\u t
由标准保证(int64\u t
不是),我们使用此类型的最小允许最大值来缩放双精度。编码例程接受int\u least64\u t
,但为了便于移植,必须拒绝大于64位的输入,对于32位的情况也是如此。长时间访问它似乎是一种未定义的行为,但是,将其转换为无符号字符数组应该是可以的。但是,是否可以保证double
和float
在每台机器上具有相同的表示形式?如果它们符合IEEE-754标准,那么唯一的问题应该是持久性。。。因此,您应该以某种类型的一致endian格式存储这些值,这样无论平台是什么,您都可以将字节数组解释为小endian或大endian。对无符号短
或无符号长
的强制转换也是如此。Endianness已经向我们表明,表示可能不同。这似乎不一致。顺便说一句,在考虑了这一点之后,而不是演员阵容,也许你应该这样做