Working with Integers

JavaScript uses the IEEE 754 encoding system to represent both integers and floats, which has caused a lot of confusion over the years. The language takes great pains to ensure that developers don’t need to worry about the details of number encoding, but problems still leak through from time to time. ECMAScript 6 seeks to address this by making integers easier to identify and work with.

Identifying Integers

First, ECMAScript 6 added the Number.isInteger() method, which can determine whether a value represents an integer in JavaScript. While JavaScript uses IEEE 754 to represent both types of numbers, floats and integers are stored differently. The Number.isInteger() method takes advantage of that, and when the method is called on a value, the JavaScript engine looks at the underlying representation of the value to determine whether that value is an integer. That means numbers that look like floats might actually be stored as integers and cause Number.isInteger() to return true. For example:

  1. console.log(Number.isInteger(25)); // true
  2. console.log(Number.isInteger(25.0)); // true
  3. console.log(Number.isInteger(25.1)); // false

In this code, Number.isInteger() returns true for both 25 and 25.0 even though the latter looks like a float. Simply adding a decimal point to a number doesn’t automatically make it a float in JavaScript. Since 25.0 is really just 25, it is stored as an integer. The number 25.1, however, is stored as a float because there is a fraction value.

Safe Integers

IEEE 754 can only accurately represent integers between -2^53^ and 2^53^, and outside this “safe” range, binary representations end up reused for multiple numeric values. That means JavaScript can only safely represent integers within the IEEE 754 range before problems become apparent. For instance, consider this code:

  1. console.log(Math.pow(2, 53)); // 9007199254740992
  2. console.log(Math.pow(2, 53) + 1); // 9007199254740992

This example doesn’t contain a typo, yet two different numbers are represented by the same JavaScript integer. The effect becomes more prevalent the further the value falls outside the safe range.

ECMAScript 6 introduced the Number.isSafeInteger() method to better identify integers that the language can accurately represent. It also added the Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER properties to represent the upper and lower bounds of the integer range, respectively. The Number.isSafeInteger() method ensures that a value is an integer and falls within the safe range of integer values, as in this example:

  1. var inside = Number.MAX_SAFE_INTEGER,
  2. outside = inside + 1;
  3. console.log(Number.isInteger(inside)); // true
  4. console.log(Number.isSafeInteger(inside)); // true
  5. console.log(Number.isInteger(outside)); // true
  6. console.log(Number.isSafeInteger(outside)); // false

The number inside is the largest safe integer, so it returns true for both the Number.isInteger() and Number.isSafeInteger() methods. The number outside is the first questionable integer value, and it isn’t considered safe even though it’s still an integer.

Most of the time, you only want to deal with safe integers when doing integer arithmetic or comparisons in JavaScript, so using Number.isSafeInteger() as part of input validation is a good idea.