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的补码签名
  • 符号和大小
以有效、简单和干净的方式(令人印象深刻,不是吗?)。但是, real类型现在是一种痛苦

我应该如何从字节流中读取
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已经向我们表明,表示可能不同。这似乎不一致。顺便说一句,在考虑了这一点之后,而不是演员阵容,也许你应该这样做