PabloRosales JavaScript Developer

JavaScript CheatSheet: Numbers

Everything about JavaScript numbers


This is a work in progress. All code examples have been run on Node 8.11.5

Intro

JavaScript, as specified in the ECMAScript standard, uses double-precision floating-point format numbers (specified in the IEEE 754) for all arithmetics. The IEEE 754 can only represent safely numbers between -(253 - 1) and 253 - 1.

Quoting the Wikipedia:

An IEEE 754 format is a "set of representations of numerical values and symbols". A format may also include how the set is encoded.

The IEEE 754 double uses the format binary64 (double-precision floating-point format). This means JavaScript stores numbers in 64 bits. The fraction part is stored in bits 0 to 51. The exponent part is stored in bits 52 to 62. The sign is stored in bit 63.

Quoting again the Wikipedia:

Floating point is used to represent fractional values, or when a wider range is needed than is provided by fixed point (of the same bit width), even if at the cost of precision.

Some numbers can't be exactly represented with binary numbers in the computer, that causes things like this to happen:

This is not just on JavaScript but any language the uses floating point format for numbers.

Integers (without decimal point or exponent notation) are accurate up to 15 digits, beyond this not all integer numbers can be represented accurately.

Here is a document (math heavy) with more info if you want to go deeper: What Every Computer Scientist Should Know About Floating-Point Arithmetic . And here is another deep dive: What Every JavaScript Developer Should Know About Floating Point Numbers . Also check How numbers are stored in JavaScript .

Zero

We have two types of zero (signed zero), negative and positive;

We'll see in the section for Infinity how this results when doing a division by zero.

We can check for negative zero using division and a comparison to zero itself, taking advantage of positive/negative Infinity:

If we try to convert negative or positive zero to a string we just get a '0':

Also if we try to search for negative/positive zero in arrays, since indexOf uses === we get the position of either positive or negative:

Same for conditional operators (ordering):

Number

JavaScript has only one number type: Number.

All numbers in JavaScript are floating-point.

Number is a wrapper object, used like this:

When the argument can't be converted to number it returns NaN. NaN is a value representing "Not a number".

A number created with the Number constructor gives us an object:

This means we can't compare it with other numbers, since they are different types:

We can get the value using valueOf:

When using the == comparison operator we do get the expected result (since this is not type strict):

If we use two Number objects we can't compare them this way:

To avoid this kind of issues we most of the times don't use wrapped Number objects, instead just use numbers.

Number is a function:

Number type conversion

Without the new keyword (non-constructor context), Number performs a type conversion.

Boolean value true is converted to 1, and false to 0:

undefined is converted into NaN

null is converted into zero:

Strings are parsed when possible, ignoring trailing and leading whitespace. Empty string is converted to zero.

For this, whitespace includes new lines, tabs, etc. For example:

Empty list is converted to zero:

If a list contains one item that is possible to convert to a number we get that number:

If our list has more than one item, or it is not possible to convert the first item into a number, we get NaN:

Empty objects and functions return NaN:

The reason behind all this is because of the process used to convert the parameter passed to Number:

  • Check if our object is a primitive
  • Check the result of valueOf
  • Check the result of toString
  • Return NaN

In this case we have a valueOf:

In this case we are using the toString method:

In the case of passing a date to Number, we get the milliseconds since midnight January 1, 1970 UTC, value that is returned by the valueOf method of the date instance, and also the method getTime.

Gotchas

When joining a number with a string, you might get something unexpected:

This is because in this case JavaScript is using the plus sign to mean string concatenation.

With subtraction tho, it converts the string into a number and then does the subtraction:

Since an empty list is converted into zero, if we add to an empty list we get something like this:

We have then to be careful what we are adding when using addition, we might get unexpected things.

In this case the ["2"] is causing all of our operation to be treated as a string concatenation.

Different formats for numbers

Numbers can be written in different formats, like Hexadecimal (base 16) starting with 0x:

Octal (base 8) format starting with a leading 0:

Octal can also be written with a leading 0o:

Binary (base 2) format starting with a leading 0b:

Using the short e-notation. This notation indicates a number that should be multiplied by 10 raised to a a power. For example, 1e9 is one billion:

The e-notation can have negative numbers too:

Javascript converts any value with at least six zeros after the decimal point into e-notation.

Different formats are just for representation and can be used with other formats:

NaN

NaN is a value representing "Not a number". This function gives answer to the question: "Is this value, when coerced to a number, an IEEE-754 'NaN' value?". You can read more on NaN for floating point in Wikipedia.

NaN is usually returned when Math functions fail:

isNaN checks if a value is NaN or not. This is useful since we can't rely on the == or === operators to check against NaN. NaN is never equal to itself.

When called without a parameter (it is undefined) we get true:

And of course NaN is a NaN:

Since NaN is never equal to NaN, we can't search for it in arrays:

NaN values are generated when arithmetic operations give us undefined or something that can't be represented as a number.

When we divide zero by itself, we get a NaN, this is because NaN is not the same as infinity, as we will see when we divide other numbers by zero.

As we will see later ECMAScript 2015 added the isNaN method to Number instances.

Another way to check if a number is NaN is checking it against itself, taking advantage of the unique "never equal to itself" feature of NaN.

When used in arithmetic operations everything is NaN:

Infinity

Infinity is a read-only global value that represent infinity. The initial value of Infinity is Number.POSITIVE_INFINITY.

Infinity is a number type:

When we divide a number by zero, we get Infinity:

If we do a negative zero division we get a negative Infinity, and with positive zero we get a positive Infinity:

Also in the case when we use Math.pow with zero and a negative number:

If we multiply any number by Infinity we get Infinity:

If we subtract Infinity to a number we get a negative Infinity:

If you try to do a subtraction of Infinity to Infinity, you get NaN. Same for division:

Trying to add to infinity or to multiply will never give you anything different;

Number properties

Number.EPSILON

Number.MAX_SAFE_INTEGER

Number.MAX_VALUE

Number.MIN_SAFE_INTEGER

Number.MIN_VALUE

Number.NaN

Number.NEGATIVE_INFINITY

Number.POSITIVE_INFINITY

Number methods

Number.isNaN(). This one is different than the global isNaN function, since this one does not convert the parameter to a number. This makes it safer to test against actual NaN, only values of type number that are NaN return true.

Number.isFinite(). This one is also different to the global version of isFinite since it does not convert the parameter to a number. Only values of type number that are finite return true.

Number.isInteger()

Number.isSafeInteger()

Number.parseFloat(). Parses the argument and returns a floating point number, if it can't convert it to float, it returns NaN.

Number.parseInt(). Takes 2 parameters, first one is a string, if it is not a string it is converted to one (using toString). The second one is the radix, usually the default is 10, but might change on implementations. Always set the radix to avoid confusion.

Number methods on instances

Number.prototype.toExponential(). Receives an option parameter to specify the number of digits after the decimal point. Returns a string, and throws RangeError if the parameter is too big/small, or TypeError if invoked in something that is not a number.

Number.prototype.toFixed(). Receives a parameter of the number of decimal points to show (between 0 and 20) and returns a string with the specified decimal points. The number is rounded if necessary, and the fractional part padded with zeros for the desired length.

Number.prototype.toLocaleString(). Returns a string with a language sensitive representation of the number. Accepts two parameters locales (string with a BCP 47 language tag, like "hi"). And the second parameter an object with options localeMatcher, style, currency, currencyDisplay, useGrouping, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, minimumSignificantDigits and maximumSignificantDigits.

Number.prototype.toPrecision(). Returns a string representing a Number object in fixed-point/exponential notation, rounded if necessary, and the fractional part padded with zeros for the desired length.

Number.prototype.toString(). Returns a string representing the number. Accepts a parameter radix between 2 to 36 for the base to use.

Number.prototype.valueOf(). Returns the primitive value of a number object.

Rounding

As we noted before, it is recommended in JavaScript to NOT compare non-integers together, this is to avoid issues with imprecise numbers like in:

If we try to compare this with 0.3 we get this:

Quoting the WikiBooks:

When a real number is rounded to the nearest floating point number, the machine epsilon forms an upper bound on the relative error.

Basically, we can use the upper bound to compare our values:

Made with by PR