A comparison of BigNumber libraries in JavaScript

fvictorio

Franco Victorio

Posted on November 6, 2019

A comparison of BigNumber libraries in JavaScript

If you've ever wanted to handle big numbers in JavaScript, you've probably noticed that there are a lot of different libraries, even for JavaScript standards. This article compares seven of them and hopefully will help you choose one.

I'll start by comparing some of the features that they do or don't support. Then I'll show some snippets of code for each one to give you a feeling of their API. After that I'll make a brief comment on the libraries used in the Ethereum ecosystem, since that's where I work on and it's an area where this kind of libraries is very present. Finally I'll give you my advice on which one to use (spoiler alert: it's big.js).

Feature comparison

The following table shows the libraries I picked and some aspects of each one. There are a lot of other things you might want to consider, like their API, performance, supported operations, etc., but this should give you a place to start.

Library Integers Floating-point Other bases Scientific Notation
big.js Yes Yes Throws Yes
bignumber.js Yes Yes Yes Yes
decimal.js Yes Yes Yes Yes
bn.js Yes Throws Yes Throws
BigInteger.js Yes Throws Yes Yes
JSBI Yes Wrong Yes Yes
jsbn Yes Wrong Yes Wrong

Integer values

All of them support integer values, but decimal.js, by design, can lose precision (more on this later).

Both BigInteger.js and JSBI can act as some sort of polyfill for the ECMAScript BigInt proposal, although their approaches differ. Check the Why? section in JSBI's readme to find out more.

Floating-point values

Only the first three support floating point numbers, and they were all developed by the same author. He wrote an explanation on how they differ, but the tl;dr is this:

  • big.js is a minimalist library. Use it if you don't need a lot of features and/or you care about the size of your dependencies.
  • bignumber.js and decimal.js are similar, the main difference is that bignumber.js expresses its precision in terms of decimals (appropriate for financial applications, for example) and decimal.js does it in terms of significant digits (better for scientific applications). That's why decimal.js is not a good choice for arbitrary integer arithmetic1.

The rest of the libraries don't support floating-point numbers, but they have different behaviors when you try to create an instance with, for example, 3.14:

  • bn.js and BigInteger.js throw an error.
  • JSBI accepts it, but it parses it as 3.
  • jsbn accepts it, but it parses it as 314.

Other bases

All of them, except big.js, support inputs in different bases. big.js throws an error if used that way. bn.js and jsbn do support different bases, but you have to be explicit: if you do new BN('0x1f3') it will return 33253 for some reason, but new BN('1f3', 16) works fine. The same comments apply to jsbn.

Scientific notation

Scientific notation works for all of them except bn.js (that throws an error) and jsbn (that, again, returns some very wrong value)2.

Show me the code

How do they look like? Let's see how to add 2+2 in each one of them. This is not enough to make a judgement on their API, but it showcases some important details:

// big.js
Big(2).add(2)

// bignumber.js
BigNumber(2).plus(2)

// decimal.js
Decimal(2).add(2)

// bn.js
new BN(2).add(new BN(2))
new BN(2).addn(2)

// BigInteger.js
BigInteger(2).add(2)

// JSBI
JSBI.add(new JSBI('2'), new JSBI('2'))

// jsbn
new jsbn.BigInteger('2').add(new jsbn.BigInteger('2'))

Enter fullscreen mode Exit fullscreen mode

There's a bunch of things you can see here:

  • Some of them require the use of new, while it's optional for the rest.
  • The add method in bn.js has to receive a BN instance as its argument. If you want to use a number, you need to use addn. jsbn requires that the argument to add be another instance.
  • Instances of JSBI don't have methods like add, you need to use the static methods of the library.
  • JSBI and jsbn require strings as the arguments to their constructors. The other libraries accept both numbers and strings.

Size and popularity

The following table shows the (minified) size of each library and their weekly number of downloads at the time of writing this:

Library Size Weekly downloads
big.js 8K 9.272.986
bignumber.js 20K 2.390.156
decimal.js 32K 290.392
bn.js 56K 7.101.573
BigInteger.js 32K 899.179
JSBI 28K 16.508
jsbn 24K 11.648.984

A note on Ethereum

Arbitrary-precision libraries are important in the Ethereum ecosystem because smart contracts can return numbers with up to 256 bits, and JavaScript can't handle that precision. That's why the main client libraries come with some sort of big number library:

  • web3@0.x uses bignumber.js (actually, a fork of it).
  • web3@1.x uses bn.js. There is a discussion about changing it again.
  • ethers.js exposes a custom big number library that uses bn.js under the hood but which also adds some extra functionality.

This means that the most used clients (web3 after 0.x and ethers.js) use a library that doesn't support floating-point numbers. This kind of makes sense, since Solidity doesn't (yet) support them, but it also makes some things harder (e.g., computing some percentage of a value).

Which one should I use?

Which library you'll choose will depend, of course, on your use case, but my advice is that you can't go wrong with big.js. The API is very nice and its feature set should cover most use cases. You can check it out and, if you need a feature that it doesn't support or if it has some behavior that makes life harder for you, then you can check some of the other ones.


  1. For example, if you are using 10 significant digits, then Decimal('22222222222222222222').div(2).toFixed(0) is equal to 11111111110000000000

  2. I think it has to do with some base inferring, but couldn't find any documentation on this. 

💖 💪 🙅 🚩
fvictorio
Franco Victorio

Posted on November 6, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related