A comparison of BigNumber libraries in JavaScript
Franco Victorio
Posted on November 6, 2019
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
anddecimal.js
are similar, the main difference is thatbignumber.js
expresses its precision in terms of decimals (appropriate for financial applications, for example) anddecimal.js
does it in terms of significant digits (better for scientific applications). That's whydecimal.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
andBigInteger.js
throw an error. -
JSBI
accepts it, but it parses it as3
. -
jsbn
accepts it, but it parses it as314
.
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'))
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 inbn.js
has to receive a BN instance as its argument. If you want to use a number, you need to useaddn
.jsbn
requires that the argument toadd
be another instance. - Instances of
JSBI
don't have methods likeadd
, you need to use the static methods of the library. -
JSBI
andjsbn
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
usesbignumber.js
(actually, a fork of it). -
web3@1.x
usesbn.js
. There is a discussion about changing it again. -
ethers.js
exposes a custom big number library that usesbn.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.
Posted on November 6, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.