I am starting to migrate a custom serialization mechanism to Protocol Buffers. One data type that will be used particularly regularly is BigDecimal
.
Does anyone know of a good way of serializing this within Protocol Buffers? Our current serialization routine uses BigDecimal.toPlainString()
for serialization, and new BigDecimal(String)
for deserialization - I'm assuming there's a better way.
My guess is to define a BigDecimal
as:
message BDecimal {
required int32 scale = 1;
required BInteger int_val = 2;
}
But I am not too sure how to define BigInteger
- perhaps using its toByteArray()
method?
The main difference between the BigInteger and BigDecimal is that BigInteger supports arbitrary-precision integers and BigDecimal is for arbitrary-precision fixed-point numbers.
This limits it to 15 to 17 decimal digits of accuracy. BigDecimal can grow to any size you need it to. Double operates in binary which means it can only precisely represent numbers which can be expressed as a finite number in binary. For example, 0.375 in binary is exactly 0.011.
A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale.
The limit is 32 * 2^32-1 bits for BigInteger or about 2^(4 billion). have a string with illegal digits.
Yes. You should define BigInteger as BigInteger.toByteArray() .
My guess is that BigDecimal would be:
message BDecimal {
required int32 scale = 1;
required BInteger int_val = 2;
}
while BigInteger may be defined as
message BInteger {
required bytes value = 1;
}
The code to handle BigInteger would be:
BInteger write(BigInteger val) {
BInteger.Builder builder = BInteger.newBuilder();
ByteString bytes = ByteString.copyFrom(val.toByteArray());
builder.setValue(bytes);
return builder.build();
}
BigInteger read(BInteger message) {
ByteString bytes = message.getValue();
return new BigInteger(bytes.toByteArray());
}
I have recently had the same need as OP, and used a similar solution to the one proposed by @notnoop. Posting it here after seeing this comment from @stikkos:
How would you convert a BigDecimal to a BigInteger and scale? And back ?
According to the BigDecimal
class documentation:
The value of the
BigDecimal
is (unscaledVal
× 10-scale
), rounded according to the precision and rounding mode settings.
So, a java.math.BigDecimal
can be serialized considering three properties:
unscaledValue
scale
precision
Now, the code.
Protobuf v3 message definition:
syntax = "proto3";
message DecimalValue {
uint32 scale = 1;
uint32 precision = 2;
bytes value = 3;
}
How to serialize a java.math.BigDecimal
to a Protobuf message:
import com.google.protobuf.ByteString;
import DecimalValue;
java.math.BigDecimal bigDecimal = new java.math.BigDecimal("1234.56789");
DecimalValue serialized = DecimalValue.newBuilder()
.setScale(bigDecimal.scale())
.setPrecision(bigDecimal.precision())
.setValue(ByteString.copyFrom(bigDecimal.unscaledValue().toByteArray()))
.build();
How to deserialize the Protobuf message back to a java.math.BigDecimal
:
java.math.MathContext mc = new java.math.MathContext(serialized.getPrecision());
java.math.BigDecimal deserialized = new java.math.BigDecimal(
new java.math.BigInteger(serialized.getValue().toByteArray()),
serialized.getScale(),
mc);
We can check the obtained java.math.BigDecimal
yields the same original value as follows:
assert bigDecimal.equals(deserialized);
Note: OP assumed there is a better way to serialize a java.math.BigDecimal
than using a plain String
. In terms of storage, this solution is better.
For example, to serialize the first 100 decimals of π:
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
System.out.println("DecimalValue: " + serialized.getSerializedSize());
System.out.println("String: " + StringValue.of(pi).getSerializedSize());
This yields:
DecimalValue: 48
String: 104
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With