Back to Blog
JavaScriptBigIntNumbersES2020

JavaScript BigInt Guide

Master JavaScript BigInt for working with arbitrarily large integers beyond Number limits.

B
Bootspring Team
Engineering
March 16, 2019
6 min read

BigInt enables working with integers larger than Number.MAX_SAFE_INTEGER. Here's how to use it effectively.

Creating BigInts#

1// Literal syntax with 'n' suffix 2const big1 = 9007199254740993n; 3 4// Constructor 5const big2 = BigInt(9007199254740993); 6const big3 = BigInt('9007199254740993'); 7 8// From hex, octal, binary 9const hex = BigInt('0x1fffffffffffff'); 10const octal = BigInt('0o377777777777777777'); 11const binary = BigInt('0b11111111111111111111111111111111111111111111111111111'); 12 13console.log(big1); // 9007199254740993n

Why BigInt?#

1// Number precision limits 2console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 3 4// Numbers lose precision 5console.log(9007199254740993); // 9007199254740992 (wrong!) 6console.log(9007199254740993 === 9007199254740992); // true (!) 7 8// BigInt maintains precision 9console.log(9007199254740993n); // 9007199254740993n (correct) 10console.log(9007199254740993n === 9007199254740992n); // false

Arithmetic Operations#

1const a = 10n; 2const b = 3n; 3 4// Basic operations 5console.log(a + b); // 13n 6console.log(a - b); // 7n 7console.log(a * b); // 30n 8console.log(a / b); // 3n (truncated, no decimals) 9console.log(a % b); // 1n 10console.log(a ** b); // 1000n 11 12// Division truncates toward zero 13console.log(5n / 2n); // 2n 14console.log(-5n / 2n); // -2n 15 16// Unary minus works 17console.log(-10n); // -10n 18 19// Unary plus doesn't work 20// console.log(+10n); // TypeError

Comparison#

1const big = 100n; 2const num = 100; 3 4// Loose equality works 5console.log(big == num); // true 6console.log(big == 100); // true 7 8// Strict equality requires same type 9console.log(big === num); // false 10console.log(big === 100n); // true 11 12// Comparison operators work 13console.log(10n > 5n); // true 14console.log(10n > 5); // true (mixed comparison OK) 15console.log(10n < 20); // true 16 17// Sorting 18const mixed = [4n, 6, -12n, 10, 4, 0, 0n]; 19mixed.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); 20console.log(mixed); // [-12n, 0, 0n, 4n, 4, 6, 10]

Type Coercion#

1// Cannot mix BigInt and Number in operations 2const big = 10n; 3const num = 5; 4 5// big + num; // TypeError: Cannot mix BigInt and other types 6 7// Convert explicitly 8console.log(big + BigInt(num)); // 15n 9console.log(Number(big) + num); // 15 10 11// Boolean conversion 12console.log(Boolean(0n)); // false 13console.log(Boolean(1n)); // true 14 15// String conversion 16console.log(String(123n)); // '123' 17console.log(123n + ''); // '123' 18 19// To Number (may lose precision) 20const huge = 9007199254740993n; 21console.log(Number(huge)); // 9007199254740992 (precision lost!)

Bitwise Operations#

1const a = 5n; // 0101 in binary 2const b = 3n; // 0011 in binary 3 4console.log(a & b); // 1n (AND: 0001) 5console.log(a | b); // 7n (OR: 0111) 6console.log(a ^ b); // 6n (XOR: 0110) 7console.log(~a); // -6n (NOT) 8console.log(a << 2n); // 20n (left shift) 9console.log(a >> 1n); // 2n (right shift) 10 11// No unsigned right shift 12// a >>> 1n; // TypeError 13 14// Useful for bit flags with large values 15const FLAG_A = 1n << 60n; 16const FLAG_B = 1n << 61n; 17const flags = FLAG_A | FLAG_B; 18console.log((flags & FLAG_A) !== 0n); // true

Practical Use Cases#

1// Large IDs (Twitter snowflake, Discord IDs) 2const twitterId = 1234567890123456789n; 3const discordId = BigInt('123456789012345678'); 4 5// Precise timestamps (nanoseconds) 6const nanoTime = BigInt(Date.now()) * 1000000n; 7 8// Cryptography 9function modPow(base, exponent, modulus) { 10 let result = 1n; 11 base = base % modulus; 12 13 while (exponent > 0n) { 14 if (exponent % 2n === 1n) { 15 result = (result * base) % modulus; 16 } 17 exponent = exponent / 2n; 18 base = (base * base) % modulus; 19 } 20 21 return result; 22} 23 24// Large factorial 25function factorial(n) { 26 let result = 1n; 27 for (let i = 2n; i <= n; i++) { 28 result *= i; 29 } 30 return result; 31} 32 33console.log(factorial(50n)); 34// 30414093201713378043612608166064768844377641568960512000000000000n

JSON Handling#

1// BigInt doesn't serialize to JSON by default 2const data = { id: 123n }; 3// JSON.stringify(data); // TypeError 4 5// Solution 1: Convert to string 6const safeData = { id: data.id.toString() }; 7console.log(JSON.stringify(safeData)); // {"id":"123"} 8 9// Solution 2: Custom serializer 10const jsonString = JSON.stringify(data, (key, value) => 11 typeof value === 'bigint' ? value.toString() : value 12); 13 14// Solution 3: Custom reviver for parsing 15const parsed = JSON.parse('{"id":"9007199254740993"}', (key, value) => { 16 if (key === 'id' && typeof value === 'string') { 17 return BigInt(value); 18 } 19 return value; 20}); 21 22// Solution 4: Prototype method (careful with this) 23BigInt.prototype.toJSON = function () { 24 return this.toString(); 25};

TypedArrays with BigInt#

1// BigInt64Array for signed 64-bit integers 2const signed = new BigInt64Array(4); 3signed[0] = 9223372036854775807n; // Max value 4signed[1] = -9223372036854775808n; // Min value 5console.log(signed); 6 7// BigUint64Array for unsigned 64-bit integers 8const unsigned = new BigUint64Array(4); 9unsigned[0] = 18446744073709551615n; // Max value 10unsigned[1] = 0n; // Min value 11console.log(unsigned); 12 13// From buffer 14const buffer = new ArrayBuffer(16); 15const view = new BigInt64Array(buffer); 16view[0] = 123n; 17view[1] = 456n;

Formatting#

1const big = 123456789012345678901234567890n; 2 3// toString with radix 4console.log(big.toString()); // decimal 5console.log(big.toString(16)); // hex 6console.log(big.toString(2)); // binary 7console.log(big.toString(36)); // base36 8 9// Locale formatting 10console.log(big.toLocaleString()); 11// '123,456,789,012,345,678,901,234,567,890' 12 13console.log(big.toLocaleString('de-DE')); 14// '123.456.789.012.345.678.901.234.567.890' 15 16// Custom formatting 17function formatBigInt(n, separator = ',') { 18 return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator); 19}

Math Operations#

1// Math methods don't work with BigInt 2// Math.max(1n, 2n); // TypeError 3// Math.sqrt(4n); // TypeError 4 5// Custom implementations 6function bigMax(...args) { 7 return args.reduce((a, b) => (a > b ? a : b)); 8} 9 10function bigMin(...args) { 11 return args.reduce((a, b) => (a < b ? a : b)); 12} 13 14function bigAbs(n) { 15 return n < 0n ? -n : n; 16} 17 18function bigSqrt(n) { 19 if (n < 0n) throw new Error('Square root of negative number'); 20 if (n < 2n) return n; 21 22 let x = n; 23 let y = (x + 1n) / 2n; 24 25 while (y < x) { 26 x = y; 27 y = (x + n / x) / 2n; 28 } 29 30 return x; 31} 32 33console.log(bigSqrt(1000000000000000000n)); // 1000000000n

Performance Considerations#

1// BigInt is slower than Number 2const iterations = 1000000; 3 4// Number performance 5console.time('Number'); 6let numSum = 0; 7for (let i = 0; i < iterations; i++) { 8 numSum += i; 9} 10console.timeEnd('Number'); 11 12// BigInt performance 13console.time('BigInt'); 14let bigSum = 0n; 15for (let i = 0n; i < BigInt(iterations); i++) { 16 bigSum += i; 17} 18console.timeEnd('BigInt'); 19 20// Use Number when values fit safely 21function safeAdd(a, b) { 22 const result = a + b; 23 if (result > Number.MAX_SAFE_INTEGER) { 24 return BigInt(a) + BigInt(b); 25 } 26 return result; 27}

Best Practices#

When to Use: ✓ IDs larger than MAX_SAFE_INTEGER ✓ Precise integer arithmetic ✓ Cryptographic calculations ✓ High-precision timestamps ✓ Bitwise ops on large numbers Type Safety: ✓ Explicit type conversion ✓ Check bounds before Number() ✓ Use typeof for type checking ✓ Handle JSON serialization Performance: ✓ Use Number when safe ✓ Avoid unnecessary conversions ✓ Cache BigInt values ✓ Profile performance-critical code Avoid: ✗ Mixing with Number in ops ✗ Using Math methods ✗ Assuming JSON support ✗ Using for small integers

Conclusion#

BigInt provides arbitrary-precision integers for values beyond Number's safe range. Use the n suffix for literals, convert explicitly between types, and remember that BigInt doesn't work with Math methods or JSON serialization by default. It's ideal for large IDs, cryptographic operations, and precise integer arithmetic. For typical operations within safe integer range, stick with Number for better performance.

Share this article

Help spread the word about Bootspring