JavaScript can only safely represent integers

in the range −2

<

< 2

. This blog post examines why that is and what “safely represent” means. It is based on an

emailby Mark S. Miller to the es-discuss mailing list.

The idea of a safe integer is about how mathematical integers are represented in JavaScript.

In the range (−2^{53}, 2^{53}) (excluding the lower and upper bounds), JavaScript integers are *safe*: there is a one-to-one mapping between mathematical integers and their representations in JavaScript.

Beyond this range, JavaScript integers are *unsafe*: two or more mathematical integers are represented as the same JavaScript integer. For example, starting at 2^{53}, JavaScript can only represent every second mathematical integer.

> Math.pow(2, 53) 9007199254740992 > Math.pow(2, 53)+1 9007199254740992 > Math.pow(2, 53)+2 9007199254740994 > Math.pow(2, 53)+3 9007199254740996 > Math.pow(2, 53)+4 9007199254740996 > Math.pow(2, 53)+5 9007199254740996 > Math.pow(2, 53)+6 9007199254740998Why is that? Simplifyingly, a number in JavaScript is represented as

mantissa × 2Mantissa and exponent give you 53 bit integers (consult [1] for the details). But you can represent higher integers by using higher exponents. For example, the 53 bit range of integers with an exponent incremented by one (a multiplication by two) becomes a 54 bit range. However, now only every second integer can be represented, as we have seen above.^{exponent}

Similarly, starting at 2^{54}, JavaScript can only represent every fourth mathematical integer (and so on).

> Math.pow(2, 54) 18014398509481984 > Math.pow(2, 54)+1 18014398509481984 > Math.pow(2, 54)+2 18014398509481984 > Math.pow(2, 54)+3 18014398509481988 > Math.pow(2, 54)+4 18014398509481988Therefore, a safe JavaScript integer is one that unambiguously represents a single mathematical integer.

Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1; Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;It will also provide a function for determining whether an integer is safe:

Number.isSafeInteger = function (n) { return (typeof n === 'number' && Math.round(n) === n && Number.MIN_SAFE_INTEGER <= n && n <= Number.MAX_SAFE_INTEGER); }For a given value

> 9007199254740990 + 3 9007199254740992We have two safe operands, but an unsafe result:

> Number.isSafeInteger(9007199254740990) true > Number.isSafeInteger(3) true > Number.isSafeInteger(9007199254740992) falseThe following result is also incorrect:

> 9007199254740995 - 10 9007199254740986This time, the result is safe, but one of the operands isn’t:

> Number.isSafeInteger(9007199254740995) false > Number.isSafeInteger(10) true > Number.isSafeInteger(9007199254740986) trueTherefore, the result of applying an integer operator

isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)implies that