JavaScript Number type

Number is one of the six primitive data types in JavaScript, unlike in other languages where numbers can be represented in multiple types such as integers, float, double etc. JavaScript only has the one numeric data type (at the moment).

The Number type is defined in the TC39 spec as in the “double-precision 64-bit format IEEE 754-2008 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic”. Understanding how numbers are stored internally, or learning this standard isn’t necessary, but it is important to understand the implications it can have on your programs.

Calculating floating point values

Probably the most common problem and source of errors is a result of storing numbers using this number standard (note this is a problem of the number standard, not a problem specific to JavaScript).

>  0.1 + 0.2
// 0.30000000000000004

Always be aware of possible precision problems when working with floating point values, if precision is required then use a function similar to the following to add the values as integers:

function addValues(...vals) {
    function decimalPlaces(x) {
        return x.toString().replace(/\d*\.?/,'').length
    }
    const mult = vals.reduce( (a,b) => {
        const dec = decimalPlaces(b);
        return dec > a ? dec : a
    }, 0)
    return vals.reduce( (a,b) => a + b * 10 ** mult, 0 ) / 10 ** mult;
}

Max integer values and safe integers

Integer values can also have problems once you reach large numbers, though this isn’t something you should encounter very often. The Number.MAX_VALUE constant represents the maximum numeric value representable in JavaScript:

>  Number.MAX_VALUE
// 1.7976931348623157e+308

Values larger than this number are stored as infinity.

JavaScript also has a Number.MAX_SAFE_INTEGER constant, which is the largest integer n such that n and n + 1 are both exactly stored. Note this number is not the same as MAX_VALUE - larger values than MAX_SAFE_INTEGER can be stored, but the value may not be correct.

>  Number.MAX_SAFE_INTEGER
// 9007199254740991
>  Number.MAX_SAFE_INTEGER +1
// 9007199254740992
>  Number.MAX_SAFE_INTEGER +2
// 9007199254740992

The above shows that Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 which isn’t correct.

This has implications when performing calculations and when parsing JSON data, which can be corrupted as a result of the coercion to the Number type:

>  JSON.parse( '{"test":9007199254740993}' )
// {test: 9007199254740992}

Zero

JavaScript has both a positive zero (+0 or just 0) and a negative zero (-0). Again this isn’t a feature of JavaScript, but comes from the IEEE 754 standard for floating-point arithmetic - “The IEEE 754 standard for floating-point arithmetic (presently used by most computers and programming languages that support floating point numbers) requires both +0 and −0.”. For practical purposes, this will have no impact on most people writing JavaScript code, as ECMAscript spec explicitly says :

d. If x is +0 and y is -0, return true.
e. If x is -0 and y is +0, return true.

So -0 will always equal +0

>  -0 === +0
//  true

And in fact, trying to test for -0 is problematic due to this equality rule

>  123 * -0
//  -0

>  123 * -0 === 0
// true

>  var test = 123 * -0
// undefined
>  test === 0
// true
>  test.toString()
// "0"

>  -0 < +0
// false

But if you do need to test for -0 you can use the following:

>  1/0
// Infinity
>  1/-0
// -Infinity

as 1 divided by -0 gives you negative Infinity, you can use this to test for a negative zero value:

const isNegative = x => (1/x) < 0

isNegative(-0) // true
isNegative(0) // false

Infinity

The Number object has properties for Number.POSITIVE_INFINITY and Number.NEGATIVE_INFINITY. The global object also has an Infinity property, which is set to Number.POSITIVE_INFINITY.

As well as checking for equality using x === Infinity we also have access to isFinite() methods for checking if a value is a finite number. There are actually two available versions of this - the global isFinite, and Number.isFinite(), the difference being that the global version will coerce any values passed to it to a number, whereas the Number method will not convert the type before checking, when using the method always be aware of which you are using as they will give different results:

>  isFinite('1')
// true
>  Number.isFinite('1')
// false

NaN

Another special number value is NaN, or Not a Number, available as a property of both global and Number. A feature of the NaN value is that it will always compare unequal to any other value - including to another NaN value

> NaN === NaN
// false
> NaN !== NaN
// true

As a result of you can check for NaN by performing a self-comparison (as all other values will return true when compared to themselves):

function myIsNaN(x) { return x !== x }

However the isNaN() function is available to do this for you. Note that, like isFinite(), the isNaN() function is available as a global function and a method on Number. Again the difference is that the global version will always coerce the value passed to it to a Number type first, so you may get unexpected results:

> isNaN('test')
// true
> Number.isNaN('test')
// false

If the second value doesn’t look correct to you, remember that the purpose of the function is not to test whether the value is numeric, but to test whether is specifically set to the NaN value.

More Reading

MDN Docs on the Number object

An excellent article on how numbers are technically represented in JavaScript by Max Koretskyi

ECMAScript spec - number object

Written on October 27, 2018