Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable precision float/double values

I have an object that I'm serializing that contains a lot of doubles and structs of doubles that I'm sending across the wire with protobuf-net. The issue is that across the wire, I don't need all of that precision.

For instance, I have something like this where an external library is returning a double.

double Volts = Sampler.GetValue(); //Volts is a value like 4.35(...)

But I really only need two decimal points of precision. Encoding this across the wire as a double takes 64 bits. Encoding it as a string with 2 digits of decimal precision ("4.35") actually may take less space. But then I have conversion issues to deal with on either side.

I've been poking around the v2 options, and I haven't seen such a capability. I can see how it could save a ton of space if you could encode floating point values to a variable length of precision.

I've thought about multiplying out to an integer sending that and then converting it back on the far end, but as far as I can tell that would require me to make significant changes to my base class objects (and I'm using the merge option during deserialization).

Any ideas or clever work arounds?

btw, protobuf-net is awesome with tons of configurability. Thanks for making such a great program.

like image 538
Mike Petty Avatar asked Sep 02 '25 18:09

Mike Petty


1 Answers

Carrying on the "maybe send them as float" discussion, you can do this pretty easily, actually; for example, if you currently have:

[ProtoMember(4)]
public double Value {get;set;}

you could change that to:

public double Value {get;set;} // this won't be serialized directly

[Protomember(4)] // but we'll use this private property for the serialization
private float ValueSingle {
    get { return (float)Value; }
    set { Value = value; }
}

and it will do the shim in the background for you. This particular change should be compatible with existing data too (although technically it does change the .proto schema, protobuf-net is fairly forgiving). This will take 4 bytes. Note also that IEEE754 applies, as always, so completely unrelated to protobuf-net there is no guarantee that any particular value (4.35, for example) can be stored exactly.

Another option, if you want fixed precision, would be to use a multiple. For example:

public double Value {get;set;}

[ProtoMember(4)]
public int ValueInt32 {
    get { return (int)Math.Round(100 * Value); }
    set { Value = value/100.0; }
}

You'd have to test to see if that is compatible with old data... that is a bigger change. Here, 4.35 will be sent as 435, which takes 2 bytes as a "varint".

like image 57
Marc Gravell Avatar answered Sep 04 '25 07:09

Marc Gravell