A comparison of BigNumber libraries in JavaScript
Tags: javascript, math, ethereum
Published: 2019-11-06 00:14:23

---
title: A comparison of BigNumber libraries in JavaScript
published: true
description: A comparison of the most popular libraries for performing arbitrary-precision arithmetic in JavaScript.
tags: javascript,math,ethereum
---

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](https://github.com/MikeMcl/big.js)                  | Yes      | Yes        | **Throws**  | Yes                 |
| [bignumber.js](https://github.com/MikeMcl/bignumber.js/)     | Yes      | Yes        | Yes         | Yes                 |
| [decimal.js](https://github.com/MikeMcl/decimal.js/)         | Yes      | Yes        | Yes         | Yes                 |
| [bn.js](https://github.com/indutny/bn.js)                    | Yes      | **Throws** | Yes  | **Throws**          |
| [BigInteger.js](https://github.com/peterolson/BigInteger.js) | Yes      | **Throws** | Yes         | Yes                 |
| [JSBI](https://github.com/GoogleChromeLabs/jsbi)             | Yes      | **Wrong**  | Yes         | Yes                 |
| [jsbn](https://github.com/andyperlitch/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](https://tc39.es/proposal-bigint/), although their approaches differ. Check the [Why?](https://github.com/GoogleChromeLabs/jsbi#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](https://github.com/MikeMcl/big.js/wiki) 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** arithmetic[^1].

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

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

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

## 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:

```javascript
// 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 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](https://github.com/MikeMcl/big.js)                  | 8K   | 9.272.986        |
| [bignumber.js](https://github.com/MikeMcl/bignumber.js/)     | 20K  | 2.390.156        |
| [decimal.js](https://github.com/MikeMcl/decimal.js/)         | 32K  | 290.392          |
| [bn.js](https://github.com/indutny/bn.js)                    | 56K  | 7.101.573        |
| [BigInteger.js](https://github.com/peterolson/BigInteger.js) | 32K  | 899.179          |
| [JSBI](https://github.com/GoogleChromeLabs/jsbi)             | 28K  | 16.508           |
| [jsbn](https://github.com/andyperlitch/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](https://github.com/frozeman/bignumber.js-nolookahead)).
- `web3@1.x` uses `bn.js`. There is [a discussion](https://github.com/ethereum/web3.js/issues/2171) 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.