Number.isSafeInteger()

Tests whether a value is an integer that IEEE-754 doubles can represent exactly.

Signature
Number.isSafeInteger(testValue)
  • Added in JavaScript ES2015

Parameters

ParameterTypeDescription
$testValueanyThe value to be tested for being a safe integer.

Return value

boolean`true` if the value is a number that is a safe integer, `false` otherwise.

Safe integers are the whole numbers from -(253 - 1) to 253 - 1, that is ±9,007,199,254,740,991. Inside that range every integer has its own distinct double. Outside it, two different integers can collapse onto the same value — 2**53 === 2**53 + 1 is true — and arithmetic silently stops being reliable.

When to reach for it

The classic case is identifiers from another system. A 64-bit database id, a Twitter-style snowflake, or a nanosecond timestamp can exceed 253, and JSON.parse will happily round it to a neighbouring value with no error. Checking the parsed number with Number.isSafeInteger() is how you find out before the wrong record is fetched. The fix is to keep such ids as strings, or to use BigInt, which has no upper bound.

How it differs from isInteger()

Number.isInteger() only asks whether the fractional part is zero, so it says true for 1e100. Number.isSafeInteger() adds the magnitude test. Note that both look only at the value in front of them: if precision was already lost during parsing, a rounded result may still land inside the safe range and pass.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Detect a 64-bit database id that lost precision in JSON
  • Decide whether a computed total needs BigInt
  • Validate a timestamp in nanoseconds before arithmetic

Frequently asked questions

What is the largest safe integer in JavaScript?

Number.MAX_SAFE_INTEGER, which is 9007199254740991 (2^53 - 1). Above it, distinct integers can share the same double and arithmetic stops being exact.

How do I handle IDs larger than MAX_SAFE_INTEGER?

Keep them as strings through JSON and convert with BigInt only when you need arithmetic. Once JSON.parse has produced a Number, the precision is already gone.

Why does Number.isSafeInteger(2 ** 53) return false?

Because 2^53 and 2^53 + 1 round to the same double, so the representation is ambiguous even though the value itself is storable.

More examples

Catching an id mangled by JSON.parse

The value was already corrupted during parsing; isSafeInteger() flags it, and comparing the stringified number with the source text confirms the loss.

Example · javascript
const payload = '{"id": 9007199254740993}';
const parsed = JSON.parse(payload);

console.log(parsed.id); // 9007199254740992 — off by one
console.log(Number.isSafeInteger(parsed.id)); // false

// Compare the round-trip against the original text
console.log(String(parsed.id) === '9007199254740993'); // false

Choosing BigInt when the range demands it

Ordinary addition at the boundary produces 9007199254740992 for both +1 and +2; BigInt keeps every digit.

Example · javascript
function addIds(a, b) {
  if (Number.isSafeInteger(a) && Number.isSafeInteger(b)) {
    return a + b;
  }
  return BigInt(a) + BigInt(b);
}

console.log(addIds(2, 3)); // 5
console.log(addIds(9007199254740991, 1)); // 9007199254740992 — a Number: both inputs were safe
console.log(addIds(2 ** 53, 2)); // 9007199254740994n — an unsafe input routes to BigInt
console.log(9007199254740991 + 2); // 9007199254740992 — wrong
console.log(BigInt('9007199254740991') + 2n); // 9007199254740993n

isInteger versus isSafeInteger

Every safe integer is an integer, but the reverse fails for any whole-valued double beyond 2^53 where exactness is gone.

Example · javascript
const values = [42, -42, 2 ** 53, 1e21, 4.0, NaN];

for (const v of values) {
  console.log(v, Number.isInteger(v), Number.isSafeInteger(v));
}
// 42                true  true
// -42               true  true
// 9007199254740992  true  false
// 1e+21             true  false
// 4                 true  true
// NaN               false false

Discussion

  • Be the first to comment on this lesson.