BigInt inside the engine
Learn how V8 stores BigInts as heap digit arrays, chooses arithmetic algorithms, converts strings, handles 64-bit paths, and enforces size limits.
- 01Read how V8 stores a BigIntName the sign, the length, and the 64-bit digits, say why the low digit comes first and why zero has no digits, and explain why even
1nis a heap object. - 02Link each operation to its costFollow a carry from one digit to the next, step through one Karatsuba split, and say which multiplication method V8 picks for a size, and why text conversion takes work.
- 03Choose the right number toolPick between Number, BigInt, BigInt64Array, and 64-bit wrapping, keep Number and BigInt math apart, and avoid promises about speed.
A BigInt is a list of digits in memory
A BigInt is JavaScript's type for whole numbers of any size, with no rounding. You write one with an n at the end, like 10n. This lesson looks at how V8 keeps a BigInt in memory. V8 is the JavaScript engine inside Chrome and Node.js. Every fact here was checked on V8 version 12.4, the version inside Node 22.
V8 keeps each BigInt on the heap. The heap is the part of memory where the engine keeps objects. A stored BigInt has three parts:
- The sign: is the number negative or not?
- The length: how many digits the number uses.
- The digits: the number cut into 64-bit pieces. One 64-bit piece holds any whole number from 0 up to 264 − 1, about 18 quintillion. V8 calls each piece a digit.
In V8, every BigInt is a heap object of type BIG_INT_BASE_TYPE. It holds a sign, a length, and its 64-bit digits, low digit first. A small Number can be a Smi: a small integer kept right inside the value, with no heap object. BigInt has no such short form. Even 1n is a heap object.
This lesson builds on the beginner BigInt lesson and Tagged values, Smis & heap numbers. It is not about how to write BigInt code. It is about how V8 stores BigInts and what each operation costs:
- why math on a BigInt always makes a new BigInt,
- why V8 multiplies in different ways for different sizes,
- why turning a BigInt into decimal text takes work,
- and how fixed 64-bit tools, like
BigInt64Array, fit in.
Picture a form where you write a long number across several boxes. One box says plus or minus. You also note how many number boxes you used. Each number box holds one part of the number. To change the number, you fill in a fresh form. You never rub out the old one.
- In real life: A plus or minus box
- In JavaScript: The sign
- In real life: A note of how many number boxes you used
- In JavaScript: The length
- In real life: Each number box holds one part
- In JavaScript: One 64-bit digit
- In real life: A changed number goes on a fresh form
- In JavaScript: Math on a BigInt gives a new BigInt
Sign, length, and 64-bit digits
The main source is V8's file src/objects/bigint.h. It describes BigIntBase, the shape every BigInt has:
- The sign and the length share one small field. The sign is 1 for a negative number and 0 for any other number.
- Each digit has the type
digit_t. It is as wide as a memory address on the machine (uintptr_t). On the 64-bit Node 22 build used by the tests, that is 64 bits. - The digits come right after, in a list that is as long as the number needs.
So 2n ** 64n, which is one more than the biggest 64-bit digit, needs two digits: 0x0 first, then 0x1. The 0x means the digit is written in hexadecimal (base 16). The low digit comes first. This order is called little-endian.
2n ** 64n is stored as 0x0, then 0x1.Each row of the table is one part of a stored BigInt. Read across: the part, what V8 shows, and what that means.
| Part | What V8 shows | What it means |
|---|---|---|
Shape (V8 calls it a map, not the JavaScript Map) | %DebugPrint shows the type BIG_INT_BASE_TYPE. The shape itself sits in read-only memory. | Every ordinary BigInt in V8 has this same shape. |
| Sign and length | One small field holds both: 1 bit for the sign and 30 bits for the length. | sign: 1 means negative. length: 0 means the number is 0n. |
| Digits | A list of uintptr_t digits, as wide as a memory address. In Node's 64-bit build, each digit is 64 bits. | The low digit comes first: 2n ** 64n prints 0x0, then 0x1. |
%DebugPrint is a debug command built into V8. It prints how a value is stored. It only works in Node, and only when you start Node with the --allow-natives-syntax flag. So this code does not run in the browser.
%DebugPrintJavaScript// Runs only in Node started with --allow-natives-syntax.for (const [label, value] of [ ["ten", 10n], ["two64", 2n ** 64n], ["negative", -5n], ["zero", 0n],]) { process.stdout.write("@@" + label + "\n"); %DebugPrint(value);}Lines 3 to 6 list four BigInts: 10n, 2n ** 64n, -5n, and 0n. Line 8 prints a label before each one, so the output is easy to split. Line 9 prints how the BigInt is stored. The lesson test checks only the parts that stay the same from run to run:
[BigInt]andBIG_INT_BASE_TYPE: the kind of object.instance size: variable: the size depends on how many digits there are.- The
length, thesign, and the digit lines.
It does not check memory addresses or exact sizes, because those change. Chrome runs a newer V8 that also squeezes addresses into 32 bits (this is called pointer compression), so the addresses there look different.
const value = 2n ** 64n;const mask = (1n << 64n) - 1n;const sign = value < 0n ? "negative" : "positive";const lowDigit = value & mask;const nextDigit = (value >> 64n) & mask; console.log(sign);console.log("0x" + lowDigit.toString(16));console.log("0x" + nextDigit.toString(16));0240 bytes64-bit digits, low digit first
- digit 0
0x0 - digit 1
0x1
18446744073709551616n has sign 0 (not negative), length 2, and the digits 0x0, 0x1, low digit first. The byte count is only a teaching estimate, not a real measurement.
& and >>. The lesson test uses %DebugPrint to check that V8 12.4 in Node stores the same digits, low digit first.The playground finds the same digits with ordinary BigInt math:
value & 0xffffffffffffffffnkeeps only the lowest 64 bits. That is the low digit.value >> 64ndrops those 64 bits, so the next digit moves down to the bottom.- It repeats until nothing is left. Zero has nothing to store, so it has length 0 and no digits.
The byte count it shows is a simple teaching estimate: 24 bytes plus 8 bytes per digit. It is not a real measurement of memory.
const cache = new Map([[1n, "a"]]);const set = new Set([1n, BigInt("1")]); console.log(1n === 1n);console.log(cache.get(1n));console.log(set.size);Each BigInt lives in its own heap object, but it still acts like a plain value, the way a number does. In plain words, line by line:
- Line 1 makes a
Mapwith one key,1n, that holds"a". - Line 2 makes a
Setfrom1nandBigInt("1").BigInt("1")turns the text"1"into a BigInt. - Line 4 prints
true.===compares the two numbers, not where they are stored. - Line 5 prints
a. AMapfinds a BigInt key by its value. - Line 6 prints
1. TheSetkeeps one copy, because both items are the same value.
Adding goes one digit at a time
V8 adds two BigInts the way you add long numbers on paper, column by column. It adds the two low digits first. If the sum is too big for one digit, it keeps the low part and carries 1 into the next digit. Subtraction works the same way, but it borrows instead of carrying.
So the work grows with the number of digits. Adding two one-digit BigInts is quick. Adding two 1,000-digit BigInts takes about 1,000 steps.
A BigInt can never be changed after it is made. So every sum is a new BigInt, and V8 must find new memory for it. In some narrow cases, V8's optimizing compiler can skip that step.
The step-through below runs a small copy of this method, written in JavaScript. Each item in the left and right lists is one 64-bit digit, low digit first.
Step through adding two BigInts one 64-bit digit at a time: add, keep the low 64 bits, carry the rest, then check against +.
script
const BIGINT_BASE = 1n << 64n;const DIGIT_MASK = BIGINT_BASE - 1n; function fromDigits(digits) { let value = 0n; for (let index = digits.length - 1; index >= 0; index -= 1) { value = value * BIGINT_BASE + digits[index]; } return value;} function addDigits(left, right) { const out = []; let carry = 0n; const length = Math.max(left.length, right.length); for (let index = 0; index < length; index += 1) { const sum = (left[index] ?? 0n) + (right[index] ?? 0n) + carry; out[index] = sum & DIGIT_MASK; carry = sum >> 64n; } if (carry) out.push(carry); return out;} const right = [0x2n];const digits = addDigits(left, right);console.log(digits.map((digit) => "0x" + digit.toString(16)).join(", "));console.log(fromDigits(digits) === fromDigits(left) + fromDigits(right));0x1, and carries 1 to the left. Digit 1 then adds 0x1 and the carry to get 0x2.The main work happens on three lines:
- Line 17 adds one digit from
left, one digit fromright, and the carry. A missing digit counts as0n. - Line 18 keeps only the low 64 bits of that sum, using
DIGIT_MASK. That is the new digit. - Line 19 shifts the sum right by 64 bits. What is left is the carry for the next digit.
Line 28 prints the new digits, 0x1, 0x2. Line 29 turns the digit lists back into BigInts with fromDigits and compares the answer with JavaScript's own +. It prints true.
Multiplying picks a method by size
Multiplying is more work than adding. The school method multiplies every digit of one number by every digit of the other. Two numbers with 100 digits each need 100 × 100 = 10,000 small multiplications. For long numbers, V8 switches to smarter methods that need fewer of them. Most are named after the people who found them:
- Schoolbook: the method you learned at school.
- Karatsuba: splits each number into two halves and saves one multiplication at each split.
- Toom-Cook: splits each number into three parts.
- FFT (fast Fourier transform): turns the multiplication into a different math problem that is quicker for huge numbers. V8's file says this method comes from Schönhage and Strassen.
V8 decides by counting the digits of the shorter number. The size where a new method starts is called a threshold. The thresholds are set in bigint-internal.h, and bigint-internal.cc uses them to choose. The 2018 V8 BigInt article describes the first version, which used only the school method.
Each line below is a constant copied from V8's source: a name and a digit count.
V8 12.4.254.21 src/bigint/bigint-internal.h:kKaratsubaThreshold = 34 digitskToomThreshold = 193 digitskFftThreshold = 1500 digitskFftInnerThreshold = 200 digitskBurnikelThreshold = 57 digitskBarrettThreshold = 13310 digitskToStringFastThreshold = 43 digitskFromStringLargeThreshold = 300 digitsLines 2 to 4 are the multiplication thresholds. Line 5 is used inside the FFT method. Lines 6 and 7 are for division, and lines 8 and 9 are for strings. Both come later in this lesson.
The table turns the thresholds into ranges. Read each row as: this method runs when the shorter number has this many digits. In the third column, n is the number of digits.
| Method | Digits in the shorter number | How the work grows | Where it is in V8 |
|---|---|---|---|
| Single digit | Exactly 1 digit. | About n steps. | MultiplySingle in src/bigint/bigint-internal.cc. |
| Schoolbook | 2 to 33 digits. | About n × n steps. | kKaratsubaThreshold = 34; mul-schoolbook.cc. |
| Karatsuba | 34 to 192 digits. | About n^1.585 steps, fewer than n × n. | kToomThreshold = 193; mul-karatsuba.cc. |
| Toom-Cook | 193 to 1499 digits. | About n^1.465 steps. | kFftThreshold = 1500; mul-toom.cc. |
| FFT | 1500 digits and up. | Grows only a little faster than n. | mul-fft.cc. On by default, except on Android. |
A build setting, v8_advanced_bigint_algorithms, turns on Toom-Cook and FFT. It is on by default, except on Android. When it is off, V8 uses Karatsuba for all large products.
The step-through below does one Karatsuba split on 123456n * 789012n.
Step through one Karatsuba split: split each number in half, make z0, z2, and the middle part z1, then put them together and check.
script
const SPLIT_BASE = 1000n; function karatsubaOneLevel(x, y) { const xLow = x % SPLIT_BASE; const xHigh = x / SPLIT_BASE; const yLow = y % SPLIT_BASE; const yHigh = y / SPLIT_BASE; const z0 = xLow * yLow; const z2 = xHigh * yHigh; const z1 = (xLow + xHigh) * (yLow + yHigh) - z2 - z0; return z2 * SPLIT_BASE * SPLIT_BASE + z1 * SPLIT_BASE + z0;} console.log(product === 123456n * 789012n);console.log(product.toString());Multiplying two split numbers the school way takes four multiplications of halves. Karatsuba needs only three:
- Line 8 multiplies the two low halves. That is
z0. - Line 9 multiplies the two high halves. That is
z2. - Line 10 adds each number's two halves, multiplies the two sums once, then subtracts
z2andz0. That leaves the middle part,z1. - Line 11 puts the three parts back together into the full product.
The example splits at 1000n, so you can read the halves in decimal. V8 splits along its 64-bit digits instead.
Division also has several methods. Read each row as: the method, when V8 uses it, and what to remember.
| Method | When V8 uses it | What to remember |
|---|---|---|
| Single digit | The number you divide by has 1 digit. | V8 divides by one 64-bit digit. |
| Schoolbook (Knuth) | The number you divide by has fewer than 57 digits. | kBurnikelThreshold = 57. V8's source says this method is loosely based on Go's and on Knuth's. |
| Burnikel-Ziegler | The number you divide by is longer, and Barrett is not picked. | It splits the problem into smaller divisions. It is in div-burnikel.cc. |
| Barrett | The extra methods are on, the number you divide by has at least 13310 digits, and the number being divided is longer still. | kBarrettThreshold = 13310. It is in div-barrett.cc. |
- 10 digits
- 33 digits
- 34 digits
- 192 digits
- 193 digits
- 1499 digits
- 1500 digits
Each card gives the digit count of the shorter number. Sort it under the method V8 12.4 uses.
Turning BigInts into text and back
A BigInt stores its value as 64-bit binary digits, not as decimal text. So toString() must work out the decimal characters, and BigInt(text) must work out the binary digits. V8 does this in two files:
tostring.ccturns a BigInt into text. For a base that is a power of two, like 16, it just regroups the bits. For other bases, like 10, it splits a large number by dividing it again and again.fromstring.ccreads text into a BigInt. For long text, it joins neighboring chunks in pairs, so the fast multiplication methods can help.
Read each row as: the kind of conversion, what V8 does, and why it matters.
| Case | What V8 does | Why it matters |
|---|---|---|
| A base that is a power of two | Regroup the bits for bases such as 2, 8, 16, and 32. | No division is needed, so it is quick. |
| A short number in another base | Multiply or divide one chunk at a time. | This simple way is fine for short numbers. |
toString on a long number | From kToStringFastThreshold = 43 digits, V8 switches methods. | It divides by big powers of the base again and again, using Barrett division. |
| Reading long text | From kFromStringLargeThreshold = 300 result digits, V8 switches methods. | It joins neighboring chunks, so the fast multiplication methods can help. |
const decimal = "900719925474099312345";const id = BigInt(decimal);const hex = id.toString(16); console.log(hex);console.log(BigInt("0x" + hex) === id);In plain words, line by line:
- Line 1 holds a long number as decimal text.
- Line 2 reads the text into a BigInt.
- Line 3 writes the BigInt as base 16 text.
- Line 5 prints
30d40000000001b6d9. - Line 6 reads the base 16 text back. The
"0x"in front tellsBigInt()it is base 16. It printstrue, so no part of the value was lost.
Printing a huge BigInt in decimal is slow because each base-10 character has to be worked out from the binary digits. No decimal text is stored ready to print.
Fixed 64-bit values and BigInt64Array
A normal BigInt grows as big as it needs. Some data has a fixed size instead: exactly 64 bits, like a number field in a network message. JavaScript has tools for that:
BigInt.asIntN(64, value)keeps only the low 64 bits and reads them as a signed number, one that can be negative.BigInt.asUintN(64, value)keeps only the low 64 bits and reads them as an unsigned number, one that is never negative.BigInt64ArrayandBigUint64Arrayare typed arrays: rows of fixed-size slots inside anArrayBuffer, which is a block of raw bytes. Each slot is 64 bits. Reading a slot gives you a BigInt.
const buffer = new ArrayBuffer(16);const signed = new BigInt64Array(buffer);const bytes = new Uint8Array(buffer); signed[0] = -1n;signed[1] = (1n << 63n) - 1n; console.log(signed[0]);console.log(bytes.slice(0, 8).every((byte) => byte === 255));console.log(BigInt.asUintN(64, -1n));In plain words, line by line:
- Line 1 makes a buffer of 16 bytes. That is room for two 64-bit slots.
- Line 2 views the buffer as signed 64-bit slots that hold BigInts.
- Line 3 views the same bytes one at a time, as numbers from 0 to 255.
- Line 5 writes
-1ninto slot 0. - Line 6 writes the biggest signed 64-bit value, 263 − 1, into slot 1.
- Line 8 prints
-1n. Reading a slot gives a BigInt, not a Number. - Line 9 prints
true. The first 8 bytes are all 255, so every bit is 1. - Line 10 prints
18446744073709551615n. Those same 64 bits, read as unsigned, are 264 − 1.
255. Reading signed[0] turns those bytes back into the BigInt -1n.V8 keeps notes on what kind of values each operation sees. Its file type-hints.h has two such notes for BigInts: kBigInt and kBigInt64. When the values always fit in 64 bits, compiler files such as simplified-lowering.cc can use plain 64-bit machine math instead of heap BigInts, in the right cases. This is a fact about how V8 can store the values. It does not promise that every BigInt expression gets faster.
The biggest BigInt
A BigInt can hold very large numbers, but not endless ones. V8 sets BigInt::kMaxLengthBits to 1 << 30, which is 230 bits, about one billion bits. The limit is in src/objects/bigint.h. A note there says V8 picks the same limit on every platform, a little below what it could handle.
// Run in Node. V8 checks the size first, so it throws before it uses the memory.try { 2n ** (2n ** 30n);} catch (error) { console.log(error.name + ": " + error.message);}Line 3 asks for 2 to the power of 230. The answer would need more than 230 bits, so V8 throws an error. Line 4 catches it, and line 5 prints its name and message.
The lesson test runs this code in a separate Node process and checks that it prints RangeError: Maximum BigInt size exceeded. V8 checks the size before it builds the number, so the error comes at once. The test never tries to build a billion-bit number.
How other engines store BigInts
Every engine follows the same ECMAScript BigInt rules, which say how a BigInt behaves. But each engine may store BigInts in its own way. Your code can't see the difference, so don't rely on it.
Read each row as: the engine, how it stores BigInts, and what its source code shows.
| Engine | How it stores BigInts | What its source shows |
|---|---|---|
| V8 12.4 (Chrome, Node) | Every BigInt is a heap object. | One field for sign and length, then 64-bit digits. No short form for small BigInts. |
| JavaScriptCore (Safari) | A short form for small BigInts exists in the source, but it is off. | USE(BIGINT32) adds a BigInt32Tag, but WebKit sets USE_BIGINT32 0. |
| SpiderMonkey (Firefox) | A heap object that keeps a few digits inside it. | JS::BigInt keeps a short digit list inside itself, and moves bigger ones to a separate list. |
- JavaScriptCore is Safari's engine. Its file
JSCJSValue.hhas a short form for small BigInts, withEncodeAsBigInt32and aBigInt32Tag. It sits behind a switch calledUSE(BIGINT32). Right now WebKit turns that switch off withUSE_BIGINT32 0. - SpiderMonkey is Firefox's engine. Its file
BigIntType.hdefinesInlineDigitsLength. A small BigInt keeps its digits inside the BigInt itself. Only a bigger one gets a separate list of digits.
Practical use
Use a Number when:
- the values stay below
Number.MAX_SAFE_INTEGER, about 9 quadrillion, - you need decimals, like
2.5, - or you need
Mathfunctions, likeMath.round().
Use a BigInt for exact large whole numbers, such as:
- database IDs,
- math for cryptography (the codes that keep data secret),
- bit masks, which are numbers used as rows of on/off switches,
- counters that go past 253,
- and 64-bit fields in network messages or file formats.
const fromProtocol = BigInt.asUintN(64, 18446744073709551616n + 5n);const smallCount = 42n; console.log(fromProtocol);console.log(Number(smallCount));Line 1 wraps 264 + 5 into 64 bits. Only 5n is left, because the 264 part does not fit. Line 4 prints 5n. Line 5 turns the small BigInt 42n into a Number on purpose and prints 42.
- Keep Number math and BigInt math apart. Convert on purpose, where data comes in or goes out.
- When a value has a fixed 64-bit size, use
BigInt.asUintN(64, value)orBigInt.asIntN(64, value). - Read a large decimal ID with
BigInt(text). Don't turn it into a Number first, because that can round it. - Measure slow code before you change it. The thresholds and 64-bit shortcuts are facts about V8, not promises about speed.
Common misconceptions
- “Small BigInts are stored without a heap object.” Not in V8 12.4. The tests show that
10nis a[BigInt]heap object. - “BigInt has no maximum.” V8 has a largest number of bits. Past it, V8 throws a
RangeError. - “BigInt64Array keeps BigInt objects in its buffer.” The buffer holds raw 64-bit slots. Reading a slot makes a BigInt.
- “Printing a BigInt in decimal is free.” Turning binary digits into decimal characters is real work.
Each row shows an idea, what is true, and the mistake people often make.
| Idea | What is true | The common mistake |
|---|---|---|
| Comparing BigInts | 1n === 1n compares the two numbers. Map and Set also compare BigInt keys by value. | Thinking it checks whether both are the same stored object. |
| Heap objects | Every V8 BigInt is a heap object, and math makes new ones. | Thinking that this makes every BigInt operation too slow to use. |
| BigInt64Array | The array holds raw 64-bit slots in an ArrayBuffer. Reading a slot makes a BigInt. | Thinking BigInt objects sit inside the buffer. |
| Biggest size | V8 refuses a BigInt above 1 << 30 bits. BigInt is limited by V8 and by memory. | Thinking a BigInt can grow forever. |
Two forms can both show the number 1. When you check the number, it doesn't matter which sheet of paper it is on. BigInt keys in a Map or Set work the same way.
- In real life: Two forms both show 1
- In JavaScript: Two expressions both give
1n - In real life: You compare the written number
- In JavaScript: Map and Set compare BigInt keys by value
- In real life: A changed total goes on a new form
- In JavaScript: Math on a BigInt gives a new BigInt
Practice exercises
2n ** 64nRun the program. Type the two digits it prints, in order.
const value = 2n ** 64n;
const mask = (1n << 64n) - 1n;
console.log("0x" + (value & mask).toString(16));
console.log("0x" + ((value >> 64n) & mask).toString(16));const value = 2n ** 64n;
const mask = (1n << 64n) - 1n;
console.log("0x" + (value & mask).toString(16));
console.log("0x" + ((value >> 64n) & mask).toString(16));The low 64 bits are all zero, so digit 0 is 0x0. The next digit is one, so digit 1 is 0x1.
Predict what the Map lookup prints.
const cache = new Map([[1n, "a"]]);
console.log(cache.get(BigInt("1")));const cache = new Map([[1n, "a"]]);
console.log(cache.get(BigInt("1")));BigInt("1") and 1n are the same value, so the Map finds the key and prints a.
Write the low digit and the carry that the addition makes.
const mask = (1n << 64n) - 1n;
const sum = 0xffffffffffffffffn + 2n;
console.log("0x" + (sum & mask).toString(16));
console.log(sum >> 64n);const mask = (1n << 64n) - 1n;
const sum = 0xffffffffffffffffn + 2n;
console.log("0x" + (sum & mask).toString(16));
console.log(sum >> 64n);The low digit is 0x1. The part that did not fit is 1, so the carry is 1n.
Run the multiplication and type the decimal product.
console.log((123456n * 789012n).toString());console.log((123456n * 789012n).toString());JavaScript's own * gives the same product as the one Karatsuba split.
-1n as an unsigned 64-bit valuePredict the unsigned value.
console.log(BigInt.asUintN(64, -1n));console.log(BigInt.asUintN(64, -1n));The low 64 bits of -1n are all ones. Read as unsigned, that is 2**64 - 1.
Use the 64-bit wrapping rule to predict what is left.
console.log(BigInt.asUintN(64, 18446744073709551616n + 5n));console.log(BigInt.asUintN(64, 18446744073709551616n + 5n));The 2**64 part does not fit in 64 bits and drops away. Only 5n is left.
Quiz
Question 1 of 8How does V8 12.4, inside Node 22, store a BigInt?
Choose an answer to see the explanation.
Question 2 of 8What length does V8 print for
0n?Choose an answer to see the explanation.
Question 3 of 8What does this BigInt key code print?
Read the code, then predictconst cache = new Map([[1n, "a"]]); const set = new Set([1n, BigInt("1")]); console.log(1n === 1n); console.log(cache.get(1n)); console.log(set.size);Choose an answer to see the explanation.
Question 4 of 8In V8 12.4, how many digits must the shorter number have before V8 switches from Schoolbook to Karatsuba?
Choose an answer to see the explanation.
Question 5 of 8What does this 64-bit BigInt code print?
Read the code, then predictconst buffer = new ArrayBuffer(8); const signed = new BigInt64Array(buffer); signed[0] = -1n; console.log(signed[0]); console.log(BigInt.asUintN(64, signed[0]));Choose an answer to see the explanation.
Question 6 of 8Why can
big.toString(10)take a long time for a huge BigInt?Choose an answer to see the explanation.
Question 7 of 8What error does V8 throw for
2n ** (2n ** 30n)?Choose an answer to see the explanation.
Question 8 of 8A network message has a 64-bit number field that wraps round at the top. What should you use?
Choose an answer to see the explanation.
Key takeaways
- V8 stores every BigInt as a heap object with a sign, a length, and 64-bit digits, low digit first.
- Zero has length 0.
2n ** 64nhas two digits:0x0, then0x1. - Adding takes one step per digit. Multiplying switches from Schoolbook to Karatsuba, Toom-Cook, and FFT as the numbers get longer.
- Turning a BigInt into text, or text into a BigInt, is real work, most of all for long decimal numbers.
- Use BigInt for exact large whole numbers. Use
asUintN(64)orasIntN(64)for fixed 64-bit values.
One-line definition: A BigInt is an exact whole number that V8 stores as a heap object with a sign, a length, and 64-bit digits, and never changes.
Up next: Symbols inside the engine.