Skip to main content

BigInt in JavaScript

JavaScript's Number type can represent integers accurately only up to a certain limit: 2^53 - 1, which is 9,007,199,254,740,991. Beyond that, numbers lose precision silently. For most everyday programming, this limit is more than enough. But for cryptography, financial calculations with large values, database IDs from systems like Twitter/X or Snowflake, high-precision timestamps, and scientific computing, this limit is a real problem.

BigInt solves this by providing a way to represent integers of arbitrary size with perfect precision. No matter how large the number, BigInt handles it exactly. This guide covers how to create BigInts, the rules for working with them (especially the strict separation from regular Numbers), how comparisons work across types, and the current state of browser support.

What Is BigInt? (Arbitrary-Precision Integers)

BigInt is a primitive type in JavaScript (added in ES2020) that can represent whole numbers larger than Number.MAX_SAFE_INTEGER (2^53 - 1) without losing precision.

The Problem BigInt Solves

// Regular numbers lose precision beyond 2^53 - 1
console.log(9007199254740991); // 9007199254740991 (correct, this is the limit)
console.log(9007199254740992); // 9007199254740992 (looks correct, but...)
console.log(9007199254740993); // 9007199254740992 (WRONG! Should be ...993)
console.log(9007199254740994); // 9007199254740994 (happens to be correct)
console.log(9007199254740995); // 9007199254740996 (WRONG!)

// The safe integer limit
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.isSafeInteger(9007199254740991)); // true
console.log(Number.isSafeInteger(9007199254740992)); // false

// Arithmetic beyond the limit is unreliable
console.log(9007199254740991 + 2); // 9007199254740992 (WRONG! Should be ...993)

Beyond MAX_SAFE_INTEGER, JavaScript's Number type (IEEE 754 double-precision floating-point) cannot represent every consecutive integer. Some integers simply cannot be stored, and operations on them produce silently incorrect results.

BigInt Has No Limit

// BigInt handles numbers of any size with perfect precision
console.log(9007199254740993n); // 9007199254740993n (correct!)
console.log(9007199254740995n); // 9007199254740995n (correct!)

// Arithmetic is always exact
console.log(9007199254740991n + 2n); // 9007199254740993n (correct!)

// Truly enormous numbers are no problem
console.log(2n ** 100n);
// 1267650600228229401496703205376n

console.log(2n ** 1000n);
// A 302-digit number, perfectly precise

// Factorial of 100
function factorial(n) {
let result = 1n;
for (let i = 2n; i <= n; i++) {
result *= i;
}
return result;
}

console.log(factorial(100n));
// 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000n
// (158 digits, completely accurate)

Real-World Scenarios

// Twitter/X snowflake IDs (64-bit integers)
const tweetId = 1762345678901234567n;
console.log(tweetId); // Precise, no rounding

// Cryptographic values
const prime = 170141183460469231731687303715884105727n; // A Mersenne prime

// Large financial calculations (amounts in smallest currency unit)
const nationalDebt = 34000000000000_00n; // $34 trillion in cents
const interestRate = 5n; // Simplified percentage
const annualInterest = nationalDebt * interestRate / 100n;
console.log(annualInterest); // 1700000000000_00n ($1.7 trillion)

// Nanosecond timestamps
const preciseTimestamp = 1705312800000000000n; // Nanoseconds since epoch

Creating BigInts: Literal and Constructor

There are two ways to create a BigInt: the literal syntax and the BigInt() constructor function.

BigInt Literals: The n Suffix

Append n to an integer literal to create a BigInt:

const a = 42n;
const b = 0n;
const c = -100n;
const d = 9007199254740993n; // Beyond MAX_SAFE_INTEGER, still precise

console.log(typeof a); // "bigint"
console.log(typeof b); // "bigint"

// Different bases work with the n suffix
const hex = 0xFFn; // 255n (hexadecimal)
const octal = 0o77n; // 63n (octal)
const binary = 0b1010n; // 10n (binary)

console.log(hex); // 255n
console.log(octal); // 63n
console.log(binary); // 10n

// Underscore separators work for readability
const trillion = 1_000_000_000_000n;
console.log(trillion); // 1000000000000n

The BigInt() Constructor

Use BigInt() (without new) to convert other values to BigInt:

// From number (must be a safe integer)
const fromNumber = BigInt(42);
console.log(fromNumber); // 42n

// From string (most common for large values)
const fromString = BigInt("9007199254740993");
console.log(fromString); // 9007199254740993n

// From hex string
const fromHex = BigInt("0xFF");
console.log(fromHex); // 255n

// From binary string
const fromBinary = BigInt("0b1010");
console.log(fromBinary); // 10n

// From octal string
const fromOctal = BigInt("0o77");
console.log(fromOctal); // 63n

What You Cannot Convert

// Floats cannot be converted (BigInt is for integers only)
// BigInt(3.14); // RangeError: The number 3.14 is not safe to convert to a BigInt

// NaN and Infinity cannot be converted
// BigInt(NaN); // RangeError
// BigInt(Infinity); // RangeError

// Invalid strings throw
// BigInt("hello"); // SyntaxError: Cannot convert hello to a BigInt
// BigInt("3.14"); // SyntaxError: Cannot convert 3.14 to a BigInt
// BigInt(""); // SyntaxError

// null and undefined throw
// BigInt(null); // TypeError
// BigInt(undefined); // TypeError

// Booleans work (true → 1n, false → 0n)
console.log(BigInt(true)); // 1n
console.log(BigInt(false)); // 0n
warning

When converting a Number to a BigInt, the number must be a safe integer (within the range where Number is precise). Converting an unsafe number would silently propagate the precision loss:

// This number already lost precision as a Number
const unsafeNumber = 9007199254740993; // Actually stored as 9007199254740992
// BigInt(unsafeNumber); // Would create 9007199254740992n (the WRONG value!)

// Always use strings for large values
const correct = BigInt("9007199254740993"); // 9007199254740993n (correct!)

new BigInt() Is Not Allowed

Unlike Number, String, and Boolean, you cannot use new with BigInt:

// WRONG: BigInt is not a constructor
// const x = new BigInt(42); // TypeError: BigInt is not a constructor

// CORRECT: Call without new
const x = BigInt(42); // 42n

Receiving BigInts from JSON

JSON does not support BigInt natively, which is a practical challenge. Large integer IDs from APIs often arrive as strings:

// JSON from an API
const jsonString = '{"id": "1762345678901234567", "name": "Alice"}';

// Parse and convert the ID to BigInt
const data = JSON.parse(jsonString);
const userId = BigInt(data.id);
console.log(userId); // 1762345678901234567n

// Sending BigInt in JSON requires conversion to string
const user = { id: 1762345678901234567n, name: "Alice" };

// JSON.stringify(user); // TypeError: Do not know how to serialize a BigInt

// Solution: custom replacer
const json = JSON.stringify(user, (key, value) => {
return typeof value === "bigint" ? value.toString() : value;
});
console.log(json); // '{"id":"1762345678901234567","name":"Alice"}'

// Or define toJSON on BigInt prototype (use with caution)
// BigInt.prototype.toJSON = function() { return this.toString(); };

BigInt Operations (No Mixing with Number)

BigInt supports all standard arithmetic and bitwise operators, but with one critical rule: you cannot mix BigInt and Number in the same operation.

Arithmetic Operators

const a = 100n;
const b = 30n;

console.log(a + b); // 130n
console.log(a - b); // 70n
console.log(a * b); // 3000n
console.log(a / b); // 3n (integer division, truncated, not rounded!)
console.log(a % b); // 10n (remainder)
console.log(a ** 3n); // 1000000n (exponentiation)

Integer Division (Truncation, Not Rounding)

BigInt division always truncates toward zero, discarding any fractional part:

console.log(7n / 2n);    // 3n (not 3.5, truncated)
console.log(-7n / 2n); // -3n (truncated toward zero, not -4)
console.log(1n / 3n); // 0n
console.log(10n / 3n); // 3n (not 3.333...)
console.log(-10n / 3n); // -3n

// If you need the remainder, use %
console.log(10n % 3n); // 1n
console.log(-10n % 3n); // -1n

This is an important difference from Number division, which returns floating-point results. If your calculation requires fractional results, BigInt is not the right tool.

Unary Operators

const x = 42n;

// Negation works
console.log(-x); // -42n
console.log(-(-x)); // 42n

// Unary + does NOT work (this is a common gotcha)
// console.log(+x); // TypeError: Cannot convert a BigInt value to a number

// Increment and decrement work
let counter = 0n;
counter++;
console.log(counter); // 1n
counter--;
console.log(counter); // 0n
danger

The unary + operator throws a TypeError with BigInt. This is intentional because +x is commonly used to convert values to Number, and implicit BigInt-to-Number conversion would lose precision. If you need to convert a BigInt to a Number, use Number() explicitly.

The No-Mixing Rule

You cannot mix BigInt and Number in arithmetic operations. This is a deliberate design choice to prevent silent precision loss:

// WRONG: Mixing BigInt and Number
console.log(1n + 2); // TypeError: Cannot mix BigInt and other types
console.log(10n * 3); // TypeError
console.log(100n / 3); // TypeError
console.log(5n ** 2); // TypeError (exponent must also be BigInt)
// CORRECT: Convert to the same type first
console.log(1n + BigInt(2)); // 3n (convert Number to BigInt)
console.log(Number(1n) + 2); // 3 (convert BigInt to Number, may lose precision!)

// Choose the direction carefully:
const bigValue = 9007199254740993n;

// Converting to Number loses precision!
console.log(Number(bigValue)); // 9007199254740992 (WRONG!)

// Keeping as BigInt preserves precision
console.log(bigValue + BigInt(2)); // 9007199254740995n (correct)

When You Must Mix Types

In practice, you often need to work with both BigInt and Number values. Always convert explicitly:

// Safe pattern: convert Numbers to BigInt when working with BigInt values
function addToBigInt(bigIntValue, regularNumber) {
return bigIntValue + BigInt(regularNumber);
}

console.log(addToBigInt(100n, 50)); // 150n

// Safe pattern: convert BigInt to Number only when you know the value is small
function bigIntToIndex(bigIntValue) {
if (bigIntValue > BigInt(Number.MAX_SAFE_INTEGER)) {
throw new RangeError("BigInt value too large to convert to Number safely");
}
return Number(bigIntValue);
}

console.log(bigIntToIndex(42n)); // 42
// bigIntToIndex(9007199254740993n); // RangeError

Bitwise Operators

All bitwise operators work with BigInt. Unlike Number (which truncates to 32-bit integers for bitwise operations), BigInt bitwise operations work on the full arbitrary-precision value:

console.log(5n & 3n);        // 1n  (AND)
console.log(5n | 3n); // 7n (OR)
console.log(5n ^ 3n); // 6n (XOR)
console.log(~5n); // -6n (NOT)
console.log(8n << 2n); // 32n (left shift)
console.log(32n >> 2n); // 8n (right shift)

// BigInt bitwise operations are NOT limited to 32 bits
const large = 2n ** 64n;
console.log(large); // 18446744073709551616n
console.log(large >> 32n); // 4294967296n (works on full value, not truncated)

// Unsigned right shift (>>>) does NOT work with BigInt
// console.log(5n >>> 1n); // TypeError
// BigInt has no concept of "unsigned" since it has arbitrary precision

Math Object Does Not Work with BigInt

The Math object methods do not accept BigInt values:

// WRONG: Math methods reject BigInt
Math.max(1n, 2n); // TypeError
Math.abs(-5n); // TypeError
Math.sqrt(16n); // TypeError
Math.pow(2n, 10n); // TypeError (use ** instead)
Math.min(1n, 2n, 3n); // TypeError
// CORRECT: Implement your own or use operators
function bigIntMax(a, b) {
return a > b ? a : b;
}

function bigIntAbs(n) {
return n < 0n ? -n : n;
}

function bigIntMin(...values) {
return values.reduce((min, val) => val < min ? val : min);
}

console.log(bigIntMax(42n, 17n)); // 42n
console.log(bigIntAbs(-99n)); // 99n
console.log(bigIntMin(5n, 3n, 8n, 1n)); // 1n

// Exponentiation uses the ** operator
console.log(2n ** 10n); // 1024n

String Conversion

BigInt converts to string naturally:

const big = 12345678901234567890n;

console.log(big.toString()); // "12345678901234567890"
console.log(big.toString(16)); // "ab54a98ceb1f0ad2" (hex)
console.log(big.toString(2)); // binary representation
console.log(big.toString(8)); // octal representation

console.log(String(big)); // "12345678901234567890"
console.log(`Value: ${big}`); // "Value: 12345678901234567890" (template literal works)
console.log("" + big); // "12345678901234567890" (string concatenation works)

Boolean Conversion

BigInt converts to boolean like Number: 0n is falsy, everything else is truthy:

console.log(Boolean(0n));   // false
console.log(Boolean(1n)); // true
console.log(Boolean(-1n)); // true
console.log(Boolean(100n)); // true

// In conditions
if (0n) {
console.log("This never runs");
}

if (42n) {
console.log("This runs - 42n is truthy");
}

// Logical operators
console.log(0n || 42n); // 42n
console.log(42n && 100n); // 100n
console.log(!0n); // true
console.log(!42n); // false

Comparisons with BigInt

Comparisons between BigInt and Number work naturally, which is an exception to the "no mixing" rule. You can compare them freely, even though you cannot perform arithmetic between them.

Equality Comparisons

// Strict equality (===): BigInt and Number are DIFFERENT types
console.log(1n === 1); // false (different types)
console.log(0n === 0); // false
console.log(0n === false); // false

// Loose equality (==): type coercion allows comparison
console.log(1n == 1); // true
console.log(0n == 0); // true
console.log(0n == false); // true (0n is falsy)
console.log(1n == true); // true

// BigInt to BigInt comparison
console.log(42n === 42n); // true
console.log(42n == 42n); // true

Relational Comparisons

Relational operators (<, >, <=, >=) work between BigInt and Number without issues:

console.log(1n < 2);     // true
console.log(2n > 1); // true
console.log(2n >= 2); // true
console.log(1n <= 1); // true

console.log(10n > 5); // true
console.log(5 > 10n); // false

// Works correctly with large values
console.log(9007199254740993n > 9007199254740991); // true
console.log(9007199254740993n > Number.MAX_SAFE_INTEGER); // true

Sorting Mixed Arrays

BigInt and Number can be sorted together:

const mixed = [4n, 2, 7n, 1, 3n, 6, 5n, 0, 8n];

// Sort works because comparisons work across types
mixed.sort((a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
});

console.log(mixed); // [0, 1, 2, 3n, 4n, 5n, 6, 7n, 8n]
info

You cannot use the common (a, b) => a - b comparator with mixed BigInt/Number arrays because subtraction between the two types throws a TypeError. Use explicit comparisons instead.

Practical Comparison Patterns

// Checking if a BigInt fits in a regular Number
function isSafeToConvert(bigIntValue) {
return bigIntValue >= BigInt(Number.MIN_SAFE_INTEGER) &&
bigIntValue <= BigInt(Number.MAX_SAFE_INTEGER);
}

console.log(isSafeToConvert(42n)); // true
console.log(isSafeToConvert(9007199254740993n)); // false

// Safe conversion with validation
function safeToNumber(bigIntValue) {
if (!isSafeToConvert(bigIntValue)) {
throw new RangeError(`BigInt ${bigIntValue} is too large for safe Number conversion`);
}
return Number(bigIntValue);
}

console.log(safeToNumber(42n)); // 42
// safeToNumber(9007199254740993n); // RangeError

BigInt in Conditional Contexts

// BigInt in if/while/for conditions
const value = 42n;

if (value) {
console.log("Truthy"); // This runs
}

// Counting down with BigInt
let countdown = 5n;
while (countdown > 0n) {
console.log(countdown); // 5n, 4n, 3n, 2n, 1n
countdown--;
}

// BigInt with ternary
const sign = value > 0n ? "positive" : value < 0n ? "negative" : "zero";
console.log(sign); // "positive"

Polyfills and Browser Support

Current Browser Support

BigInt has been supported in all modern browsers since 2020:

BrowserVersionYear
Chrome67+2018
Firefox68+2019
Safari14+2020
Edge (Chromium)79+2020
Node.js10.3+2018
Deno1.0+2020

The only notable absence is Internet Explorer, which will never support BigInt (it is discontinued).

Checking for Support

// Feature detection
if (typeof BigInt === "function") {
console.log("BigInt is supported");
const big = BigInt("9007199254740993");
// Use BigInt safely
} else {
console.log("BigInt is NOT supported");
// Use a library or alternative approach
}

// In a utility function
function createBigInt(value) {
if (typeof BigInt === "undefined") {
throw new Error("BigInt is not supported in this environment");
}
return BigInt(value);
}

Polyfill Limitations

BigInt is a primitive type with special syntax (the n suffix) and operator support. A true polyfill is not possible because you cannot add new syntax or change how operators work in JavaScript. The n suffix and native operator overloading cannot be polyfilled.

What exists are library alternatives that provide BigInt-like functionality through objects and method calls:

// Using JSBI (Google's BigInt library that works as a polyfill target)
// npm install jsbi
import JSBI from "jsbi";

const a = JSBI.BigInt("9007199254740993");
const b = JSBI.BigInt("12345");

// Cannot use operators: must use method calls
const sum = JSBI.add(a, b);
const product = JSBI.multiply(a, b);
const isGreater = JSBI.greaterThan(a, b);

console.log(sum.toString()); // "9007199254740993" + 12345
console.log(isGreater); // true

JSBI is designed to be used with a Babel plugin that automatically converts JSBI method calls to native BigInt syntax when targeting modern browsers. This gives you the best of both worlds: compatibility with older environments during development, and native BigInt performance in production for modern browsers.

Transpilation Strategy

If you need to support older browsers:

// Write code with native BigInt
const a = 9007199254740993n;
const b = a + 1n;

// Babel plugin @nicolo-ribaudo/plugin-proposal-bigint
// can transpile this to JSBI calls:
const a = JSBI.BigInt("9007199254740993");
const b = JSBI.add(a, JSBI.BigInt("1"));

When to Use a Library vs. Native BigInt

ScenarioRecommendation
Modern browsers onlyUse native BigInt
Must support IE11Use JSBI or a big-number library
Node.js 10.3+Use native BigInt
Financial precision (decimals)Use a decimal library (e.g., decimal.js, big.js)
Only need numbers up to 2^53Regular Number is fine
tip

BigInt handles integers only. If you need arbitrary-precision decimal numbers (for currency, scientific calculations with decimal points), BigInt is not the right tool. Use libraries like decimal.js, big.js, or bignumber.js for arbitrary-precision decimal arithmetic.

// BigInt: integers only
console.log(10n / 3n); // 3n (truncated, no decimals)

// For precise decimals, use a library:
// import Decimal from 'decimal.js';
// new Decimal(10).div(3); // 3.3333333333333333333...

Practical BigInt Usage in Modern Code

// Working with database IDs (commonly 64-bit integers)
class SnowflakeId {
#id;

constructor(id) {
this.#id = typeof id === "bigint" ? id : BigInt(id);
}

// Extract timestamp from a Discord/Twitter-style snowflake
get timestamp() {
// Discord epoch: 2015-01-01
const discordEpoch = 1420070400000n;
const ms = (this.#id >> 22n) + discordEpoch;
return new Date(Number(ms));
}

get workerId() {
return Number((this.#id & 0x3E0000n) >> 17n);
}

get sequence() {
return Number(this.#id & 0xFFFn);
}

toString() {
return this.#id.toString();
}

toJSON() {
return this.#id.toString(); // Safe for JSON serialization
}
}

const messageId = new SnowflakeId("1192345678901234567");
console.log(messageId.timestamp); // Date object
console.log(messageId.toString()); // "1192345678901234567"

Summary

ConceptKey Takeaway
What BigInt isA primitive type for arbitrary-precision integers; no upper limit on size
Literal syntaxAppend n to a number: 42n, 0xFFn, 0b1010n
ConstructorBigInt("12345") or BigInt(42); no new keyword
No mixing with NumberArithmetic between BigInt and Number throws TypeError; convert explicitly
Integer division7n / 2n is 3n (truncated toward zero, no decimals)
Unary +Does not work with BigInt; use Number() for explicit conversion
Math objectDoes not work with BigInt; use operators or custom functions
ComparisonsWork freely between BigInt and Number (1n < 2 is true)
Strict equality1n === 1 is false (different types); 1n == 1 is true
Boolean conversion0n is falsy; all other BigInts are truthy
JSONNot supported natively; convert to string for serialization
Browser supportAll modern browsers since 2020; no IE support; cannot be truly polyfilled
Not for decimalsBigInt is integers only; use decimal libraries for fractional precision

BigInt fills a real gap in JavaScript by providing unlimited integer precision. Its strict separation from the Number type prevents the silent precision loss that causes devastating bugs in financial, cryptographic, and ID-handling code. Use BigInt whenever you work with integers that might exceed 2^53, and always be explicit about conversions between BigInt and Number.