Subscribe to PHP Freaks RSS

bigint-money: an NPM package for doing currency math

syndicated from planet-php.net on January 23, 2019

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:



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)