Lesson 36 of 80
Every JS number is a 64-bit float underneath, which is why 0.1 + 0.2 isn't 0.3 and why huge integers quietly lose accuracy. BigInt is the escape hatch.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
JavaScript has one numeric type for everything — 1, 1.5, 1e300 are all just Number, stored as an IEEE-754 double-precision float. That format is great at representing a huge range of values but bad at representing most decimal fractions exactly, and it can only safely represent integers up to a certain size.
Past that size, or when you need exact decimal math, Number isn't the wrong tool because you're holding it wrong — it's the wrong tool, full stop. BigInt is a second, separate numeric type built specifically for arbitrary-size whole numbers.
Fraction precision:
0.1 + 0.2;
// 0.30000000000000004
Neither 0.1 nor 0.2 has an exact binary floating-point representation — same reason 1/3 has no exact finite decimal representation. The engine stores the closest double it can, and the rounding error shows up when you add them. This is not a JS bug; every language using IEEE-754 doubles does this.
Integer precision:
Number.MAX_SAFE_INTEGER;
// 9007199254740991 (2^53 - 1)
Number.MAX_SAFE_INTEGER + 1;
// 9007199254740992 -- correct, barely
Number.MAX_SAFE_INTEGER + 2;
// 9007199254740992 -- wrong! should be ...993
Past 2^53 - 1, there are more integers than the double format has room to distinguish, so multiple mathematically different integers collapse onto the same stored value. This bites for real with things like database bigint IDs (Twitter/X snowflake IDs, Postgres bigint primary keys) that arrive as JSON numbers — parse one into a JS Number and it can silently change value.
BigInt fixes the integer case:
const big = 9007199254740993n; // the `n` suffix makes it a BigInt literal
big + 1n;
// 9007199254740994n -- exact
typeof big;
// "bigint"
BigInt("9007199254740993"); // same value, built from a string
1n + 1 throws TypeError: Cannot mix BigInt and other types. You must convert explicitly — Number(1n) + 1 or 1n + BigInt(1) — and converting a BigInt outside Number.MAX_SAFE_INTEGER back to Number loses precision again, which defeats the point.=== behaves, == behaves, comparisons behave — but be deliberate. 1n === 1 is false (different types), but 1n == 1 is true and 1n < 2 works fine, because </> do coerce for comparison. Don't rely on this loosely; it's an easy source of confusion.JSON.stringify can't serialize BigInt at all. It throws TypeError: Do not know how to serialize a BigInt, not silently drop it. If a value might be a BigInt, convert to a string before stringifying and parse it back deliberately on the other end.This is the same underlying limitation you meet in equality and coercion — JS having far fewer types than the values it needs to represent, and every corner case tracing back to that. BigInt is the language admitting Number can't do everything and shipping a second type instead of further overloading the first.