JavaScript for impatient programmers (beta)
Please support this book: buy it or donate
(Ad, please don’t block.)

14. Numbers

This chapter covers JavaScript’s single type for numbers, number.

14.1. JavaScript only has floating point numbers

You can express both integers and floating point numbers in JavaScript:

98
123.45

However, there is only a single type for all numbers: They are all doubles, 64-bit floating point numbers implemented according the the IEEE Standard for Floating-Point Arithmetic (IEEE 754).

Integers are simply floating point numbers without a decimal fraction:

> 98 === 98.0
true

Note that, under the hood, most JavaScript engines are often still able to use real integers, with all associated performance and storage size benefits.

14.2. Number literals

Let’s examine literals for numbers.

14.2.1. Integer literals

Several integer literals let you express integers with various bases:

// Binary (base 2)
assert.equal(0b11, 3);

// Octal (base 8)
assert.equal(0o10, 8);

// Decimal (base 10):
assert.equal(35, 35);

// Hexadecimal (base 16)
assert.equal(0xE7, 231);

14.2.2. Floating point literals

Floating point numbers can only be expressed in base 10.

Fractions:

> 35.0
35

Exponent: eN means ×10N

> 3e2
300
> 3e-2
0.03
> 0.3e2
30

14.2.3. Syntactic pitfall: properties of integer literals

Accessing a property of an integer literal entails a pitfall: If the integer literal is immediately followed by a dot then that dot is interpreted as a decimal dot:

7.toString(); // syntax error

There are four ways to work around this pitfall:

7.0.toString()
(7).toString()
7..toString()
7 .toString()  // space before dot

14.3. Number operators

14.3.1. Binary arithmetic operators

assert.equal(1 + 4, 5); // addition
assert.equal(6 - 3, 3); // subtraction

assert.equal(2 * 1.25, 2.5); // multiplication
assert.equal(6 / 4, 1.5); // division
assert.equal(6 % 4, 2); // remainder

assert.equal(2 ** 3, 8); // exponentiation

% is a remainder operator (not a modulo operator) – its result has the sign of the first operand:

> 3 % 2
1
> -3 % 2
-1

14.3.2. Unary plus and negation

assert.equal(+(-3), -3); // unary plus
assert.equal(-(-3), 3); // unary negation

Both operators coerce their operands to numbers:

> +'123'
123

14.3.3. Incrementing (++) and decrementing (--)

The incrementation operator ++ exists in a prefix version and a suffix version and destructively adds one to its operand. Therefore, its operand must be a storage location, so that it can be changed. The decrementation operator -- works the same, but subtracts one from its operand. The next two examples explain the difference between the prefix and the suffix versions.

Prefix ++ and prefix --: change and then return.

let foo = 3;
assert.equal(++foo, 4);
assert.equal(foo, 4);

let bar = 3;
assert.equal(--bar, 2);
assert.equal(bar, 2);

Suffix ++ and prefix --: return and then change.

let foo = 3;
assert.equal(foo++, 3);
assert.equal(foo, 4);

let bar = 3;
assert.equal(bar--, 3);
assert.equal(bar, 2);
14.3.3.1. Operands: not just variables

You can also apply these operators to property values:

const obj = { a: 1 };
++obj.a;
assert.equal(obj.a, 2);

And to Array elements:

const arr = [ 4 ];
arr[0]++;
assert.deepEqual(arr, [5]);

  Exercise: Number operators

exercises/numbers-math/is_odd_test.js

14.4. Converting to number

Three ways of converting values to numbers:

Recommendation: use the descriptive Number().

Examples:

assert.equal(Number(undefined), NaN);
assert.equal(Number(null), 0);

assert.equal(Number(false), 0);
assert.equal(Number(true), 1);

assert.equal(Number(123), 123);

assert.equal(Number(''), 0);
assert.equal(Number('123'), 123);
assert.equal(Number('xyz'), NaN);

How objects are converted to numbers can be configured via several special methods. For example, .valueOf():

> Number({ valueOf() { return 123 } })
123

  Exercise: Converting to number

exercises/numbers-math/parse_number_test.js

14.5. Error values

Two number values are returned when errors happen:

14.6. Error value: NaN

NaN is an abbreviation of “not a number”. Ironically, JavaScript considers it to be a number:

> typeof NaN
'number'

When is NaN returned?

NaN is returned if a number can’t be parsed:

> Number('$$$')
NaN
> Number(undefined)
NaN

NaN is returned if an operation can’t be performed:

> Math.log(-1)
NaN
> Math.sqrt(-1)
NaN

NaN is returned if an operand or argument is NaN (to propagate errors):

> NaN - 3
NaN
> 7 ** NaN
NaN

14.6.1. Checking for NaN

NaN is the only JavaScript value that is not strictly equal to itself:

const n = NaN;
assert.equal(n === n, false);

These are several ways of checking if a value x is NaN:

const x = NaN;

assert.ok(Number.isNaN(x)); // preferred
assert.ok(x !== x);
assert.ok(Object.is(x, NaN));

14.6.2. Finding NaN in Arrays

Some Array methods can’t find NaN:

> [NaN].indexOf(NaN)
-1

Others can:

> [NaN].includes(NaN)
true
> [NaN].findIndex(x => Number.isNaN(x))
0
> [NaN].find(x => Number.isNaN(x))
NaN

14.7. Error value: Infinity

When is the error value Infinity returned?

Infinity is returned if a number is too large:

> Math.pow(2, 1023)
8.98846567431158e+307
> Math.pow(2, 1024)
Infinity

Infinity is returned if there is a division by zero:

> 5 / 0
Infinity
> -5 / 0
-Infinity

14.7.1. Infinity as a default value

Infinity is larger than all other numbers (except NaN), making it a good default value:

function findMinimum(numbers) {
  let min = Infinity;
  for (const n of numbers) {
    if (n < min) min = n;
  }
  return min;
}

assert.equal(findMinimum([5, -1, 2]), -1);
assert.equal(findMinimum([]), Infinity);

14.7.2. Checking for Infinity

These are two common ways of checking if a value x is Infinity:

const x = Infinity;

assert.ok(x === Infinity);
assert.ok(!Number.isFinite(x));

  Exercise: Comparing numbers

exercises/numbers-math/find_max_test.js

14.8. The precision of numbers: careful with decimal fractions

Internally, JavaScript floating point numbers are represented with base 2 (according the the IEEE 754 standard). That means that decimal fractions (base 10) can’t always be represented precisely:

> 0.1 + 0.2
0.30000000000000004
> 1.3 * 3
3.9000000000000004
> 1.4 * 100000000000000
139999999999999.98

You therefore need to take rounding errors into consideration when performing arithmetic in JavaScript.

Read on for an explanation of this phenomenon.

  Quiz: basic

See quiz app.

14.9. (Advanced)

All remaining sections of this chapter are advanced.

14.10. Background: floating point precision

In this section, we explore how JavaScript represents floating point numbers internally. It uses three numbers to do so (which take up a total of 64 bits of storage):

The value of a represented number is computed as follows:

(–1)sign × 0b1.fraction × 2exponent

To make things easier to understand, we make two changes:

How does this representation work for numbers with decimals (digits after the decimal point)? We move the trailing point of an integer (the mantissa), by multiplying it with 10 to the power of a negative exponent.

For example, this is how we move the trailing point of 15 so that it becomes 1.5:

> 15 * (10 ** -1)
1.5

This is another example. This time, we move the point by two digits:

> 325 * (10 ** -2)
3.25

If we write negative exponents as fractions with positive exponents, we can see why some fractions can be represented as floating point numbers, while others can’t:

14.11. Integers in JavaScript

Integers are simply (floating point) numbers without a decimal fraction:

> 1 === 1.0
true
> Number.isInteger(1.0)
true

14.11.1. Converting to integer

The recommended way of converting numbers to integers is to use one of the rounding methods of the Math object (which is documented in the next chapter):

Tbl. 4 shows the results of these functions for various inputs.

Table 4: Functions for converting numbers to integers.
-2.9 -2.5 -2.1 2.1 2.5 2.9
Math.floor -3 -3 -3 2 2 2
Math.ceil -2 -2 -2 3 3 3
Math.round -3 -2 -2 2 3 3
Math.trunc -2 -2 -2 2 2 2

14.11.2. Ranges of integers in JavaScript

It’s good to be aware of the following ranges of integers in JavaScript:

14.11.3. Safe integers

Recall that this is how JavaScript represents floating point numbers:

(–1)sign × 0b1.fraction × 2exponent

For integers, JavaScript mainly relies on the fraction. Once integers grow beyond the capacity of the fraction, some of them can still be represented as numbers (with the help of the exponent), but there are now gaps between them.

For example, the smallest positive unsafe integer is 2 ** 53:

> 2 ** 53
9007199254740992
> 9007199254740992 + 1
9007199254740992

We can see that the JavaScript number 9007199254740992 represents both the corresponding integer and the corresponding integer plus one. That is, at this point, only every second integer can be represented precisely by JavaScript.

The following properties of Number help determine if an integer is safe:

Number.isSafeInteger() can be implemented as follows.

function isSafeInteger(n) {
  return (typeof n === 'number' &&
    Math.trunc(n) === n &&
    Number.MIN_SAFE_INTEGER <= n &&
    n <= Number.MAX_SAFE_INTEGER);
}
14.11.3.1. Safe computations

Let’s look at computations involving unsafe integers.

The following result is incorrect and unsafe, even though both of its operands are safe.

> 9007199254740990 + 3
9007199254740992

The following result is incorrect, but safe. Only one of the operands is unsafe.

> 9007199254740995 - 10
9007199254740986

Therefore, the result of an expression a op b is correct if and only if:

isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)

  Exercise: Safe integers

exercises/numbers-math/is_safe_integer_test.js

14.12. Bitwise operators

14.12.1. Binary bitwise operators

Table 5: Binary bitwise operators.
Operation Name
num1 & num2 Bitwise And
num1 | num2 Bitwise Or
num1 ^ num2 Bitwise Xor

The binary operators (tbl. 5) combine the bits of their operands to produce their results:

> (0b1010 & 0b11).toString(2)
'10'
> (0b1010 | 0b11).toString(2)
'1011'
> (0b1010 ^ 0b11).toString(2)
'1001'

14.12.2. Bitwise Not

Table 6: The bitwise Not operator.
Operation Name
~num Bitwise Not, ones’ complement

The bitwise Not operator (tbl. 6) inverts each binary digit of its operand:

> bin(~0b100)
'11111111111111111111111111111011'

bin() returns a binary representation of its argument and is implemented as follows.

function bin(n) {
  // >>> ensures highest bit isn’t interpreted as a sign
  return (n >>> 0).toString(2).padStart(32, '0');
}

14.12.3. Bitwise shift operators

Table 7: Bitwise shift operators.
Operation Name
num << count Left shift
num >> count Signed right shift
num >>> count Unsigned right shift

The shift operators (tbl. 7) move binary digits to the left or to the right:

> (0b10 << 1).toString(2)
'100'

>> preserves highest bit, >>> doesn’t:

> bin(0b10000000000000000000000000000010 >> 1)
'11000000000000000000000000000001'
> bin(0b10000000000000000000000000000010 >>> 1)
'01000000000000000000000000000001'

14.13. Quick reference: numbers

14.13.1. Converting to number

Tbl. 8 shows what happens if you convert various values to numbers via Number().

Table 8: Converting values to numbers.
x Number(x)
undefined NaN
null 0
boolean false 0, true 1
number x (no change)
string '' 0
other parse number, ignoring whitespace
object configurable (.valueOf(), .toString(), Symbol.toPrimitive)

14.13.2. Arithmetic operators

JavaScript has the following arithmetic operators:

Table 9: Binary arithmetic operators.
Operator Name Example
_ + _ Addition ES1 3 + 4 7
_ - _ Subtraction ES1 9 - 1 8
_ * _ Multiplication ES1 3 * 2.25 6.75
_ / _ Division ES1 5.625 / 5 1.125
_ % _ Remainder ES1 8 % 5 3
-8 % 5 -3
_ ** _ Exponentiation ES2016 6 ** 2 36
Table 10: Prefix and suffix arithmetic operators
Operator Name Example
+_ Unary plus ES1 +(-7) -7
-_ Unary negation ES1 -(-7) 7
_++ Increment ES1 let x=0; [x++, x] [0, 1]
++_ Increment ES1 let x=0; [++x, x] [1, 1]
_-- Decrement ES1 let x=1; [x--, x] [1, 0]
--_ Decrement ES1 let x=1; [--x, x] [0, 0]

14.13.3. Bitwise operators

JavaScript has the following bitwise operators:

Operands and results of bitwise operators:

Helper function for displaying binary numbers:

function bin(x) {
  // >>> ensures highest bit isn’t interpreted as a sign
  return (x >>> 0).toString(2).padStart(32, '0');
}
Table 11: Bitwise And, Or, Xor, Not.
Operator Name Example
_ & _ Bitwise And ES1 (0b1010 & 0b1100).toString(2) '1000'
_ | _ Bitwise Or ES1 (0b1010 | 0b1100).toString(2) '1110'
_ ^ _ Bitwise Xor ES1 (0b1010 ^ 0b0011).toString(2) '1001'
~_ Bitwise Not ES1 ~0b11111111111111111111111111111110 1
Table 12: Bitwise shift operators.
Operator Name Example
_ << _ Left shift ES1 (0b1 << 1).toString(2) '10'
_ >> _ Signed right shift ES1 bin(0b10000000000000000000000000000010 >> 1)
'11000000000000000000000000000001'
_ >>> _ Unsigned right shift ES1 bin(0b10000000000000000000000000000010 >>> 1)
'01000000000000000000000000000001'

14.13.4. Number.* data properties

14.13.5. Number.* methods

14.13.6. Number.prototype.*

14.13.7. Sources

  Quiz: advanced

See quiz app.