JavaScript can only safely represent integers
iin the range −2
53<
i< 2
53. 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 (−253, 253) (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 253, 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 × 2exponentMantissa 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.
Similarly, starting at 254, 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 n, this function first checks whether n is a number and whether it is an integer. If both checks succeed, n is safe if it is greater or equal to MIN_SAFE_INTEGER and less or equal to MAX_SAFE_INTEGER.
> 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 op is only guaranteed to be correct if all operands and the result are safe. More formally:
isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)implies that a op b is a correct result.