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 Weekly downloads big.js Yes Yes Throws Yes 9.272.986 bignumber.js Yes Yes Yes Yes 2.390.156 decimal.js Yes Yes Yes Yes 290.392 bn.js Yes Throws Yes Throws 7.101.573 BigInteger.js Yes Throws Yes Yes 899.179 JSBI Yes Wrong Yes Yes 16.508 jsbn Yes Wrong Yes Wrong 11.648.984

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.

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.

and throw an error. JSBI accepts it, but it parses it as 3 .

accepts it, but it parses it as . 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 ' ))

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.

, 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.

method in has to receive a BN instance as its argument. If you want to use a number, you need to use . that the argument to be another instance. Instances of JSBI don't have methods like add , you need to use the static methods of the library.

don't have methods like , 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.

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).

uses (actually, a fork of it). web3@1.x uses bn.js . There is a discussion about changing it again.

uses . There is a discussion about changing it again. ethers.js uses bn.js .

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.