JavaScript Safe Integer Range

JavaScript `Number` integers are exactly representable only from `-(2^53 - 1)` through `2^53 - 1`. Outside this range, integer arithmetic can lose precision because `Number` uses IEEE 754 double-precision floating-point representation.

JavaScript's `Number` type represents numeric values using IEEE 754 binary64 floating point. It has 53 bits of integer precision, so every integer is represented exactly only through `2^53 - 1`, which is `9,007,199,254,740,991`, in either direction.

The safe range is `Number.MIN_SAFE_INTEGER` through `Number.MAX_SAFE_INTEGER`. Within this interval, an integer can be represented exactly and comparisons between distinct integers behave as expected. `Number.isSafeInteger(value)` checks both that the value is an integer and that it lies within this exact range.

Beyond the safe range, different mathematical integers can map to the same `Number` value. For example, `Number.MAX_SAFE_INTEGER + 1` and `Number.MAX_SAFE_INTEGER + 2` produce the same numeric result, so using ordinary `Number` values for large integer identifiers or counters can silently corrupt data.

When exact integers larger than the safe range are required, JavaScript provides `BigInt`. A `BigInt` literal uses an `n` suffix, such as `9007199254740993n`. `Number` and `BigInt` should not be mixed directly in arithmetic; convert deliberately when crossing between the two representations.

Key points

Interview questions

What is the JavaScript safe integer range, and why does it end at `2^53 - 1`?

The safe integer range is from `-(2^53 - 1)` to `2^53 - 1`. JavaScript `Number` uses IEEE 754 binary64 floating point, which provides 53 bits of precision for integer values. Once the magnitude exceeds this boundary, not every consecutive integer has a distinct representable `Number` value. That is why integer calculations can lose precision beyond the safe range.

What is the difference between `Number.isInteger()` and `Number.isSafeInteger()`?

`Number.isInteger()` determines whether a value is an integer according to its numeric representation. It does not require the integer to be exactly representable within JavaScript's safe range. `Number.isSafeInteger()` is stricter: it requires an integer value that lies between `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`. Therefore, an unsafe integer can pass `Number.isInteger()` while failing `Number.isSafeInteger()`.

Why can `Number.MAX_SAFE_INTEGER + 1` equal `Number.MAX_SAFE_INTEGER + 2`?

At that magnitude, binary64 no longer has enough precision to represent every consecutive integer. Both mathematical integers are rounded to the same representable floating-point value. As a result, JavaScript can make the comparison between those two computed `Number` values return `true`. This is a precision problem, not a normal arithmetic overflow.

When should a frontend application use `BigInt` instead of `Number`?

Use `BigInt` when the application must preserve exact integer values outside the safe `Number` range. Common examples include very large identifiers, counters, or values received from an API that exceed `2^53 - 1`. The tradeoff is that `BigInt` has different operators and cannot be freely mixed with `Number`. Serialization and interoperability with APIs also need explicit handling because JSON does not natively represent `BigInt` values.