Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data serialization in C?

I have this structure which I want to write to a file:

typedef struct
{
    char* egg;
    unsigned long sausage;
    long bacon;
    double spam;
} order;

This file must be binary and must be readable by any machine that has a C99 compiler.

I looked at various approaches to this matter such as ASN.1, XDR, XML, ProtocolBuffers and many others, but none of them fit my requirements:

  • small
  • simple
  • written in C

I decided then to make my own data protocol. I could handle the following representations of integer types:

  • unsigned
  • signed in one's complement
  • signed in two's complement
  • signed in sign and magnitude

in a valid, simple and clean way (impressive, no?). However, the real types are being a pain now.

How should I read float and double from a byte stream? The standard says that bitwise operators (at least &, |, << and >>) are for integer types only, which left me without hope. The only way I could think was:

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);

but that doesn't seem really efficient. I also could not find a description of the representation of double and float. How should one proceed before this?

like image 237
pudiva Avatar asked Mar 24 '26 01:03

pudiva


2 Answers

If you want to be as portable as possible with floats you can use frexp and 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);
}

The Idea behind this is, that ldexp, frexp and INT_MAX are standard C. Also the precision of an unsigned long is usually at least as high as the width of the mantissa (no guarantee, but it is a valid assumption and I don't know a single architecture that is different here).

Therefore the conversion works without precision loss. The division/multiplication with INT_MAX may loose a bit of precision during conversion, but that's a compromise one can live with.

like image 81
Nils Pipenbrinck Avatar answered Mar 26 '26 15:03

Nils Pipenbrinck


If you are using C99 you can output real numbers in portable hex using %a.

like image 30
lhf Avatar answered Mar 26 '26 13:03

lhf