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
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|
All of them support integer values, but
decimal.js, by design, can lose precision (more on this later).
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.jsis a minimalist library. Use it if you don't need a lot of features and/or you care about the size of your dependencies.
decimal.jsare similar, the main difference is that
bignumber.jsexpresses its precision in terms of decimals (appropriate for financial applications, for example) and
decimal.jsdoes it in terms of significant digits (better for scientific applications). That's why
decimal.jsis 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,
BigInteger.jsthrow an error.
JSBIaccepts it, but it parses it as
jsbnaccepts it, but it parses it as
All of them, except
big.js, support inputs in different bases.
big.js throws an error if used that way.
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
Scientific notation works for all of them except
bn.js (that throws an error) and
jsbn (that, again, returns some very wrong value)2.
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.
bn.jshas to receive a BN instance as its argument. If you want to use a number, you need to use
jsbnrequires that the argument to
addbe another instance.
- Instances of
JSBIdon't have methods like
add, you need to use the static methods of the library.
jsbnrequire strings as the arguments to their constructors. The other libraries accept both numbers and strings.
The following table shows the (minified) size of each library and their weekly number of downloads at the time of writing this:
bignumber.js(actually, a fork of it).
bn.js. There is a discussion about changing it again.
ethers.jsexposes a custom big number library that uses
bn.jsunder 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 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.