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