bn.js: arbitrary precision integers for the parts of JavaScript that need them
BigNum in pure javascript
At a glance
- What is it?
- bn.js is Fedor Indutny's pure JavaScript big number library, the arithmetic layer behind a lot of Node cryptography, with Montgomery reduction and Mersenne prime contexts for elliptic curve work. It handles integers only, and its API encodes its trade-offs in the method names.
- Who is it for?
- Adopt bn.js if you need arbitrary precision integer arithmetic in JavaScript, in Node or in a browser, and you are working with values that are naturally integral such as keys, curves or protocol fields. Do not adopt it for decimal quantities, because the library states that decimals are not supported, and consider the native BigInt type first for a new project.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 108 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Big numbers as a library rather than a language feature
bn.js is described in one line, BigNum in pure javascript, and the package description calls it a big number implementation in pure javascript. The library is by Fedor Indutny, and the reason it exists is worth understanding before you install it. JavaScript's native number type is a double, and beyond 2 to the 53 a Number cannot represent every integer exactly, which is why arithmetic on large integers silently loses precision. The language's own answer to that problem arrived later in the form of BigInt, and that is the context in which bn.js sits. It predates BigInt by years, it runs on any JavaScript engine including old browsers, and it was written for a world where the alternative was to hand-roll a bignum class. The install is one command:
npm install --save bn.jsand the first use is two constructors and a call. The README states that the library is optimised for elliptic curves that work with 256-bit numbers, and, importantly, that there is no limitation on the size of the numbers, so the optimisation target is a statement about what it is tuned for rather than a cap. The library is MIT licensed, and the tree has a lib/ directory with the implementation, a test/ directory, a benchmarks/ directory and a util/ directory.
Reading the method names: the i and u prefixes
The API is dense, and the documentation spends a lot of space on how to read a method name, which is the right way to learn it. Prefixes appear in the order they are listed. The prefix i means the operation is performed in place, storing the result in the host object on which the method was invoked, and the documentation says it might be used to avoid number allocation costs. The prefix u means unsigned: ignore the sign of the operands when performing the operation, or always return a positive value. That second case applies to reduction operations like mod, and in such cases if the result would be negative, modulo is added to the result to make it positive. So a.umod(b) reduces a modulo b and returns a positive value, while a.mod(b) keeps the sign of the dividend. The examples in the README are compact, a.iadd(b) performs addition on a and b storing the result in a, a.umod(b) reduces a modulo b returning a positive value, and a.iushln(13) shifts the bits of a left by 13 with both the in-place and unsigned modifiers. The convention pays for itself the first time you read a method name in a traceback.
The n suffix and the 0x4000000 ceiling
The postfixes tell you what type crosses the boundary between the library and the rest of your code. The postfix n means the argument of the function must be a plain JavaScript Number, decimals are not supported, and the number passed must be smaller than 0x4000000, which is 67,108,864, otherwise an error is thrown. The postfix rn means both the argument and the return value of the function are plain JavaScript Numbers, again with no decimals. This matters more than it first appears. A method that takes a plain Number argument cannot take a bignum argument, and a method that returns a plain Number cannot return a value that does not fit in one, so the suffix is a promise about losslessness in both directions. And the ceiling is not the 53-bit limit of a double, it is much lower, which tells you the authors were being conservative about what a plain Number argument is safe to be. If you see an n suffix in a method you are calling, you are in code that trades the library's generality for interoperability with ordinary numbers, and the error thrown when the argument is too large is a deliberate guard rather than a bug. A related utility is a.toNumber(), documented as converting to a JavaScript Number with a limit of 53 bits, which is the other place where a silent precision loss is possible.
Reduction contexts, Montgomery multiplication and Mersenne primes
The part of bn.js that separates it from a general bignum library is fast reduction, and it is a separate section of the README because it changes how you call the arithmetic. The scenario is doing lots of reductions using the same modulus, where tricks like Montgomery multiplication or a special algorithm for a Mersenne prime pay off. The mechanism is a reduction context. You create one with BN.red(num) where num is a BN instance, or with BN.red(primeName) where the name is one of the supported Mersenne primes, k256, p224, p192 or p25519, or with BN.mont(num) to reduce using the Montgomery trick. The documentation notes that .mont() is generally faster than .red(num) but slower than BN.red(primeName), which is the expected ordering since a named Mersenne prime allows the most specialised reduction. The lifecycle has three steps, and skipping them is the usual mistake: convert the inputs into the context, operate on them there, and convert the outputs back. Conversion in is a.toRed(red) and conversion out is redA.fromRed(). Inside the context, most operations have a red counterpart, redAdd, redIAdd, redSub, redISub, redShl, redMul, redIMul, redSqr, redISqr, redNeg, redPow for modular exponentiation, redInvm for the modular inverse, and redSqrt for a square root modulo the context's prime.
The utility layer and the boundary with Node
The utility methods are the ones you will use in almost every program, and they cover conversion, inspection and comparison. a.clone() copies a number. a.toString(base, length) converts to a base string and pads with zeroes, which is the form you need for fixed-width fields. a.toJSON() converts to a JSON compatible hex string and is an alias of toString(16), so a bignum serialises to JSON as a hex string rather than a number, which is the only safe choice for values beyond a double's precision. a.toArray(endian, length) converts to a byte array and optionally zero-pads to a length, throwing if the value already exceeds it, and a.toArrayLike(type, endian, length) converts to an instance of a type that must behave like an Array. a.toBuffer(endian, length) converts to a Node.js Buffer if available, and the documentation recommends a.toArrayLike(Buffer, endian, length) instead for compatibility with browserify and similar tools. The inspection side gives bitLength, zeroBits, byteLength, isNeg, isEven, isOdd and isZero, and the comparison side gives cmp returning -1, 0 or 1, plus the convenience predicates lt, lte, gt, gte and eq. There is also a toTwos and fromTwos pair for two's complement representation with an explicit bit width, which is how you convert a signed value into an unsigned field of a given size.
No decimals, and the ES5 constraint behind the design
The README states in bold that decimals are not supported in this library, and it repeats the point in every n and rn suffix description. That is a hard boundary rather than a gap, and it has a technical reason. A bignum that supports fractions is a different data structure with a different cost model, and a library that claims to do one thing well is better at it. The consequence for an adopter is that any value with a fractional part, such as a price, a rate or a percentage, is out of scope, and if your domain has those you need a different approach. The other design constraint is visible in the manifest rather than the README. The development dependencies include eslint-plugin-es5, which means the source is held to an ES5 standard even though the runtime is modern Node, and that is a deliberate choice about what the library can be bundled into. The browser field maps buffer to false, and the files array ships only lib/bn.js, so the package is one file with no dependencies. The build and test story is correspondingly plain, with standardx for linting, mocha for the unit tests run as `mocha --reporter=spec test/*-test.js`, and the test script running lint before unit.
Where bn.js is the wrong tool
Three cases stand out. The first is decimal arithmetic, which the library rules out by design, and if you need exact fractional values in JavaScript then this is not a candidate. The second is a new project on a modern runtime, where the native BigInt type already provides arbitrary precision integers with the arithmetic operators built into the language, and bn.js's advantage over it is that it works on engines without BigInt rather than that it is better. If your deployment target includes browsers you do not control, that advantage is real, and if it does not, the built-in type is usually the better first choice. The third is the maintenance picture, which deserves a close look. The newest release listed is v5.2.1 from 2022-05-24, with v5.2.0 on the same day, while the manifest declares version 5.2.5, so the published artefacts and the working tree are on different version numbers. The last push was on 2026-07-09, which is recent enough to say the work has not stopped, but there is no release cadence to point at. A library that is a dependency of a cryptography stack needs a predictable update path, and a repository whose version numbering is out of step with its tags is where that conversation starts.
Editorial conclusion
Adopt bn.js if you need arbitrary precision integer arithmetic in JavaScript, in Node or in a browser, and you are working with values that are naturally integral such as keys, curves or protocol fields. Do not adopt it for decimal quantities, because the library states that decimals are not supported, and consider the native BigInt type first for a new project. Before you depend on it, note the release gap, since the published tag listed is v5.2.1 while the manifest reads 5.2.5, and check the last push on 2026-07-09 against how quickly you need a fix.
Frequently asked questions
How do I install and use bn.js?
Install it with `npm install --save bn.js`, then require the package and construct values with a base, for example `new BN('dead', 16)` for a hexadecimal value and `new BN('101010', 2)` for a binary one. Adding them with a.add(b) and printing with res.toString(10) gives the decimal result.
Does bn.js support decimal numbers?
No. The README states in bold that decimals are not supported in this library, and repeats the restriction in the descriptions of the n and rn method suffixes.
What do the i and u prefixes mean in bn.js method names?
The i prefix performs the operation in place, storing the result in the host object, which avoids number allocation. The u prefix means unsigned, ignoring the sign of the operands or always returning a positive value, which applies to reduction operations such as mod.
What is a reduction context in bn.js?
It is a context for fast modular reduction, created with BN.red(num), BN.red(primeName) for a Mersenne prime such as k256, p224, p192 or p25519, or BN.mont(num) for Montgomery multiplication. You convert numbers in with toRed, operate on them there, and convert them back with fromRed.
What licence is bn.js released under?
The MIT License, with the licence file in the repository root. The author is Fedor Indutny. The latest listed release is v5.2.1 from 2022-05-24 while the manifest declares 5.2.5, and the last push was on 2026-07-09.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/indutny-bn-js)Was this useful?