MikeMcl / decimal.js

An arbitrary-precision Decimal type for JavaScript
http://mikemcl.github.io/decimal.js
MIT License
6.35k stars 480 forks source link

Parsing from binary (Python encoding via avro) #231

Open tlindener opened 8 months ago

tlindener commented 8 months ago

Hi,

first of thanks a lot for the library! It's a real lifesaver in the node world :-)

In one of my projects I'm trying to read avro encoded messages (originally encoded in python) in nodejs. In avro the decimal is converted to bytes and provided a logical type with precision 38 and scale 20. Since this goes beyond 64 bits the existing solutions like this do not work. https://github.com/ovotech/castle/blob/main/packages/avro-decimal/src/index.ts

We also tried it with your extension functions https://github.com/MikeMcl/decimal.js-extensions/tree/master/serialize But the binary representations don't seem to be compatible. May be you have some ideas to solve this?

MikeMcl commented 7 months ago

I have no experience with avro but I read that it stores decimals using the two’s-complement representation of the unscaled integer value in big-endian byte order, so the following function should work.

It assumes that the decimals will be positive values. If not, then test the highest bit of the first byte and if it is set then invert all the bits by subtracting each byte from 256 before convertion to hex, then add one to the BigInt result.

// Must be greater or equal to 38 if you are decoding values with a precision of 38.
Decimal.precision = 40;

const bufferToDecimal = (buf, scale) => new Decimal(
  BigInt(buf.reduce((hex, byte) => hex + byte.toString(16).
  padStart(2, '0'), '0x')).toString()
).div(10 ** scale);