bigint-money: an NPM package for doing currency math
Not long ago, I was confused about doing math with money and floating point issues. After a bunch of research and help from friends I figured out what many already did before me: Using floating points is indeed bad. You can get rounding issues and really want to use ‘precise math’.
After that, I realized I wanted to use the new Ecmascript bigint type as a basis for representing money. This is supported by recent versions of Node.js and Typescript, which is where I need it.
There’s a problem though, the bigint
type only holds ‘whole numbers’ and
doesn’t do fixed-point decimal-math. I looked around on NPM for something that
would abstract this for me, but couldn’t find a library.
So I spent a couple of days writing a simple npm library called bigint-money
that wraps the bigint
type. It’s written in Typescript and open-source so
it’s here for anyone to use: https://www.npmjs.com/package/bigint-money.
Major features:
- Uses the the Ecmascript bigint type.
- Written in Typescript.
- Loosely follows Martin Fowler’s Money Type from “Patterns of Enterprise Application Architecture”.
- Faster than Money packages that use non-native bigdecimal libraries.
- All rounding is done via the “Bankers Rounding” (a.k.a. “round half to even”).
- Uses 12 decimals for any calculations.
Benchmark
Most ‘money’ libraries on NPM only use 2 digits for precision, or use Javacript’s “number” and will quickly overflow.
The only comparible library I found was big-money. It’s probably
the best alternative if your Javascript environment doesn’t have support
for bigint
yet.
My simple benchmark calculates a ledger with 1 million entries.
bigint-money | big-money
ledger 816 ms | 43.201 ms
% 100 % | 5294 %
The benchmark script can be found in the repository.
I want to stress though that I can’t take credit for this (pretty good) result.
The library is fast because bigint
is fast.
I did need to implement a few functions myself, and I actually feel that somebody who’s better at writing fast code could probably do a much better job than I did.
I imagine that specifically the moneyValueToBigInt
and bigintToFixed
intenral
functions could probably be optimized by someone much smarter than me.
Examples
This is how it generally works:
// Creating a money object.
import Money from 'bigint-money';
const foo = new Money('5', 'USD');
It’s possible to create a new money object with a Number as well
const foo = new Money(5, 'USD');
However, if you pass it a number that’s ‘unsafe’ such as a float, an error will be thrown:
const foo = new Money(.5, 'USD');
// Unsa
Truncated by Planet PHP, read more at the original (another 10860 bytes)