npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

bigint-money

v2.0.0

Published

A Money class for high precision calculations using the ESnext bigint type.

Downloads

11,924

Readme

bigint-money

This library can be used for doing math with Money. Key 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 %

If you want to run it yourself, you can find my test script in the bench/ directory.

Installation

npm i bigint-money

Usage

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');
// UnsafeIntegerException

Once you have a Money object, you can use toFixed() to output a string.

const foo = new Money('5', 'USD');
console.log(foo.toFixed(2)); // 5.00

Arithmetic

You can use .add() and .subtract() on it:

const foo = new Money('5', 'USD');
const bar = foo.add('10');

All Money objects are immutable. Calling those functions does not change the original value:

console.log(foo.toFixed(2), bar.toFixed(2));
// 5.00 1.00

You can also pass Money objects to subtract and add:

const startBalance = new Money(1000, 'USD');
const salary = new Money(2000, 'USD');
const newBalance = startBalance.add(salary);

If you try to add money from different currencies, an error will be thrown:

new Money(1000, 'USD').add( new Money( 50000, 'YEN' ));
// IncompatibleCurencyError

Division and multiplication:

// Division
const result = new Money(10).divide(3);

// Multiplication
const result = new Money('2000').multiply('1.25');

// Powers
const result = new Money(2).pow(8);

Comparing objects

The Money object has the following functions for comparisons:

  • isLesserThan
  • isGreaterThan
  • isEqual
  • isLesserThanOrEqual
  • isGreaterThanOrEqual

All of them can take a (safe) number, a string or another Money object and return a boolean.

There is also a compare() function that returns -1, 0 or -1 depending on if the passed argument was more, equal or less than the object.

const money1 = new Money('1.00', 'EUR');

money.compare(2); // Returns -1
money.compare(1); // Returns 0
money.compare(0); // Returns 1

money.compare('0.01'); // returns -1
money.compare(new Money('1.000005', 'EUR')); // returns 1

money.compare(new Money('1', 'CAD')); // throws IncompatibleCurrencyError

The idea is that if the object is smaller than the passed one, -1 returned. 0 is returned if they're equal and 1 is returned if the passed value is higher.

This makes it easy to sort:

const values = [
  new Money('1', 'USD'),
  new Money('2', 'USD')
];

values.sort( (a, b) => a.compare(b) );

Allocate

When splitting money in parts, it might be possible to lose a penny. For example, when dividing $1 between 3 people, each person gets $ 0.33 but there's a spare $ 0.01.

The allocate function splits a Money value in even parts, but the remainder is distributed over the parts round-robin.

const earnings = new Money(100, 'USD');

console.log(
  earnings.allocate(3, 2);
);

// Results in 3 Money objects:
//   33.34
//   33.33
//   33.33

Splitting debts (negative values) also works as expected.

The second argument of the allocate function is the precision. Basically the number of digits you are interested in.

For USD and most currencies this is 2. It's required to pass this argument because the Money object can't guess the desired precision.

Rounding

By default this library uses 'round half to even' aka 'bankers rounding', but a different rounding method may be specified in the constructor.

import { Money, Round } from 'bigint-money';
const m = new Money(100, 'USD', Round.HALF_AWAY_FROM_0);

Common rounding techniques round to the nearest integer, but require a tie-breaker for the 0.5 case. These are rounding options for that case:

  • Round.HALF_TO_EVEN - The default
  • Round.BANKERS - Alias of 'HALF_TO_EVEN'
  • Round.AWAY_FROM_0 - Round away from 0. (up if positive, down if negative)
  • Round.HALF_TOWARDS_0 - Round towards 0. (down if positive, up if negative).

These rounding options don't always go the nearest integer

  • Round.TOWARDS_0 - Always rounds towards 0. This effectively just drops the fraction.
  • Round.TRUNCATE - Alias for TOWARDS_0.

Why is this library needed?

Floating points and money

Using floating points for money can be problematic when rounding, you don't always get what you expect.

Because of this, developers tend to multiply their currencies by a 100, so instead of 5 dollars, they might count 500 cents.

This ensures that there are no rounding problems.

However, this might start to get problematic if a lot of digits are needed. Javascript automatically converts integers to floats once they are larger than 9,007,199,254,740,991.

When counting cents instead of dollars, this still gives us a maximum of 90 trillion dollars. Some financial calculations require more more precision than cents though.

For example, there are cryptocurrencies such as Monero that count up 12 digits. This means if we want to precisely count monero, this gives us a maxium of 9007 monero, which currently is around $390,000 USD.

Even in traditional accounting and finance, it might be required to have more significant digits.

Bigint libraries in javascript

Traditionally this is solved in Javascript by using one of the 'bigint' or bignumber' libraries. Some examples:

The way these libraries work is that they use strings for numbers, split the number up somehow and do arthimetic sometimes 1 digit at a time.

This is fairly complex, and not very fast.

Bigint in EcmaScript

Future versions of Ecmascript will have support for a bigint type. This type is a new type of 'number', but unlike the 'Number' type it doesn't automatically convert to floating point numbers and can be extremely large.

The way you might see a bigint in a source file is like this:

const foo = 10n + 5n;

The n prefix tells the javascript engine this is no Number, but a Bigint.

There's a lot more info on the Google Blog.