How JavaScript Implements BigInt for Large Numbers

JavaScript’s BigInt primitive allows developers to represent and manipulate integers beyond the safe limit of the standard Number type (\(2^{53} - 1\)). While standard numbers use 64-bit double-precision floating-point format (IEEE 754), JavaScript engines like V8 and SpiderMonkey implement BigInt using arbitrary-precision arithmetic (often called “bignum”). This architecture splits large values across dynamically allocated arrays of machine-word integers and uses specialized algorithms to compute results across those segments.

The Memory Representation: Digits and Limbs

Standard CPU registers operate on fixed bit-widths (typically 32-bit or 64-bit). Because a BigInt can have virtually unlimited magnitude, the engine cannot store it in a single hardware register.

Instead, JavaScript engines allocate an internal data structure containing: * A sign bit: Indicates whether the number is positive or negative. * A length indicator: Tracks the number of chunks currently in use. * An array of “digits” (or “limbs”): The raw numeric value stored in base \(2^{32}\) (on 32-bit platforms) or base \(2^{64}\) (on 64-bit platforms).

For example, on a 64-bit architecture, a number requiring 128 bits is stored as two consecutive 64-bit integers. If an operation exceeds the capacity of the current limbs, the engine dynamically reallocates memory to add another limb.

Heap Allocation and Immutability

Unlike primitive Number values, which engines can often store inline directly on the stack or inside CPU registers, BigInt instances are typically heap-allocated objects under the hood.

Despite being stored on the heap like objects, BigInt retains primitive semantics in JavaScript: * Immutability: Every arithmetic operation creates a completely new BigInt structure rather than mutating existing limbs. * Garbage Collection: Intermediate BigInt values generated during complex computations must be collected by the JavaScript garbage collector once they fall out of scope.

Some engines apply small-integer optimizations. When a BigInt value fits inside standard pointer bits (using pointer tagging techniques), engines can avoid a full heap allocation, though many implementations keep all BigInt representations unified on the heap for consistency.

Arithmetic Implementation

Because the hardware CPU does not have instructions for arbitrary-precision math, JavaScript engines implement arithmetic using multi-precision algorithms:

Performance Trade-Offs

Because BigInt relies on software-level emulation rather than direct hardware instructions, its operations are significantly slower than standard 64-bit floats or 32-bit integers. The overhead comes from dynamic memory management, cache misses associated with pointer-chasing on the heap, and multi-step loop execution for multi-limb arithmetic.