Learning Objectives
- Understand BigInt primitive type
- Create and use BigInt values
- Perform BigInt arithmetic
- Handle BigInt conversions
- Apply BigInt best practices
What is BigInt?
BigInt is a primitive type introduced in ES2020 for representing integers larger than Number.MAX_SAFE_INTEGER (2^53 - 1). It allows arbitrary precision integer arithmetic.
The Number Limitation Problem
// JavaScript numbers lose precision beyond this
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
// Precision loss
console.log(9007199254740991 + 1); // 9007199254740992 ✓
console.log(9007199254740991 + 2); // 9007199254740992 ✗ (should be 993)
console.log(9007199254740991 + 3); // 9007199254740994 ✓
// BigInt solves this
console.log(9007199254740991n + 1n); // 9007199254740992n ✓
console.log(9007199254740991n + 2n); // 9007199254740993n ✓
console.log(9007199254740991n + 3n); // 9007199254740994n ✓
Creating BigInt Values
// Literal with 'n' suffix
const big1 = 123n;
const big2 = 9007199254740993n;
// BigInt() constructor
const big3 = BigInt(456);
const big4 = BigInt("789012345678901234567890");
// From hex, octal, binary
const hex = BigInt("0x1fffffffffffff");
const oct = BigInt("0o777");
const bin = BigInt("0b11111111");
console.log(typeof big1); // "bigint"
Important: BigInt() cannot convert non-integers
BigInt(3.14); // Error: Cannot convert 3.14 to a BigInt
BigInt("3.14"); // Error: Cannot convert "3.14" to a BigInt
Arithmetic Operations
const a = 100n;
const b = 50n;
// Basic operations
console.log(a + b); // 150n
console.log(a - b); // 50n
console.log(a * b); // 5000n
console.log(a / b); // 2n
console.log(a % b); // 0n
console.log(a ** 2n); // 10000n
// Division truncates (no decimals)
console.log(7n / 2n); // 3n (not 3.5)
console.log(10n / 3n); // 3n (not 3.333...)
// Negative numbers
console.log(-5n * 3n); // -15n
console.log(10n / -3n); // -3n
Comparisons
// Strict equality checks type
console.log(1n === 1); // false (different types)
console.log(1n === BigInt(1)); // true
// Loose equality allows coercion
console.log(1n == 1); // true
console.log(0n == 0); // true
// Relational operators work across types
console.log(1n < 2); // true
console.log(2n > 1); // true
console.log(5n >= 5); // true
console.log(10n <= 20); // true
// Sorting
const mixed = [5n, 2, 10n, 1, 8n];
mixed.sort((a, b) => {
a = BigInt(a);
b = BigInt(b);
return a < b ? -1 : a > b ? 1 : 0;
});
console.log(mixed); // [1, 2, 5n, 8n, 10n]
Type Conversion
// BigInt to Number (may lose precision!)
const big = 123n;
const num = Number(big); // 123
console.log(typeof num); // "number"
// Large BigInt loses precision
const largeBig = 9007199254740993n;
const largeNum = Number(largeBig); // 9007199254740992 (WRONG!)
// Number to BigInt
const bigFromNum = BigInt(456); // 456n
// String conversion
const str = big.toString(); // "123"
const bigFromStr = BigInt("789"); // 789n
// Boolean conversion
console.log(Boolean(0n)); // false
console.log(Boolean(1n)); // true
console.log(Boolean(-1n)); // true
Real-World Examples
Precise Timestamps
// Nanosecond precision timestamps
class PreciseTimer {
constructor() {
this.start = process.hrtime.bigint();
}
elapsed() {
const end = process.hrtime.bigint();
return end - this.start;
}
elapsedMs() {
return Number(this.elapsed() / 1000000n);
}
}
const timer = new PreciseTimer();
// ... some operation ...
console.log(`Elapsed: ${timer.elapsedMs()}ms`);
Large ID Generation
class IdGenerator {
constructor() {
this.counter = 0n;
this.timestamp = BigInt(Date.now());
}
generate() {
this.counter++;
// Combine timestamp and counter
return (this.timestamp * 1000000n) + this.counter;
}
}
const generator = new IdGenerator();
console.log(generator.generate()); // 1234567890123456001n
console.log(generator.generate()); // 1234567890123456002n
Factorial of Large Numbers
function factorial(n) {
if (n <= 1n) return 1n;
let result = 1n;
for (let i = 2n; i <= n; i++) {
result *= i;
}
return result;
}
console.log(factorial(20n)); // 2432902008176640000n
console.log(factorial(100n)); // Huge number!
Limitations and Gotchas
1. Cannot mix BigInt and Number
// Error: Cannot mix BigInt and other types
// 1n + 1;
// 5n * 2;
// Must convert explicitly
const result = 1n + BigInt(1); // 2n
const result2 = Number(5n) * 2; // 10
2. No Math library support
// Math methods don't work with BigInt
// Math.sqrt(4n); // Error
// Math.pow(2n, 3n); // Error
// Use BigInt operators instead
console.log(2n ** 3n); // 8n
3. JSON doesn't support BigInt
const obj = { id: 123n };
// Error: Do not know how to serialize a BigInt
// JSON.stringify(obj);
// Custom serialization
const json = JSON.stringify(obj, (key, value) =>
typeof value === 'bigint' ? value.toString() + 'n' : value
);
// Custom parsing
const parsed = JSON.parse(json, (key, value) =>
typeof value === 'string' && value.endsWith('n')
? BigInt(value.slice(0, -1))
: value
);
Best Practices
1. Use BigInt for large integers only
// Good - beyond safe integer range
const largeId = 9007199254740993n;
// Unnecessary - regular number works fine
const small = 42n; // Just use 42
2. Be explicit with conversions
function add(a, b) {
// Convert to BigInt explicitly
return BigInt(a) + BigInt(b);
}
3. Handle division carefully
// Remember: BigInt division truncates
const result = 7n / 2n; // 3n, not 3.5
// For precision, convert to Number
const precise = Number(7n) / Number(2n); // 3.5
Key Takeaways
- BigInt handles arbitrarily large integers
- Created with 'n' suffix or BigInt() function
- Cannot mix with Number without explicit conversion
- Integer division truncates decimals
- Perfect for timestamps, IDs, and cryptography
- No Math library support
- Requires custom JSON serialization
- Use only when precision beyond Number is needed