Chapter 11. Numbers
Table of contents
Buy the book
(Ad, please don’t block.)

Chapter 11. Numbers

JavaScript has a single type for all numbers: it treats all of them as floating-point numbers. However, the dot is not displayed if there are no digits after the decimal point:

> 5.000

Internally, most JavaScript engines optimize and do distinguish between floating-point numbers and integers (details: Integers in JavaScript). But that is something that programmers don’t see.

JavaScript numbers are double (64-bit) values, based on the IEEE Standard for Floating-Point Arithmetic (IEEE 754). That standard is used by many programming languages.

Number Literals

A number literal can be an integer, floating point, or (integer) hexadecimal:

> 35  // integer
> 3.141  // floating point
> 0xFF  // hexadecimal


An exponent, eX, is an abbreviation for “multiply with 10X”:

> 5e2
> 5e-2
> 0.5e2

Invoking Methods on Literals

With number literals, the dot for accessing a property must be distinguished from the decimal dot. This leaves you with the following options if you want to invoke toString() on the number literal 123:

123 .toString()  // space before the dot

Converting to Number

Values are converted to numbers as follows:

Value Result





A boolean



A number

Same as input (nothing to convert)

A string

Parse the number in the string (ignoring leading and trailing whitespace); the empty string is converted to 0. Example: '3.141'3.141

An object

Call ToPrimitive(value, Number) (see Algorithm: ToPrimitive()—Converting a Value to a Primitive) and convert the resulting primitive.

When converting the empty string to a number, NaN would arguably be a better result. The result 0 was chosen to help with empty numeric input fields, in line with what other programming languages did in the mid-1990s.[14]

Manually Converting to Number

The two most common ways to convert any value to a number are:


(Invoked as a function, not as a constructor)


I prefer Number(), because it is more descriptive. Here are some examples:

> Number('')
> Number('123')
> Number('\t\v\r12.34\n ')  // ignores leading and trailing whitespace

> Number(false)
> Number(true)


The global function parseFloat() provides another way to convert values to numbers. However, Number() is usually a better choice, as we shall see in a moment. This code:


converts str to string, trims leading whitespace, and then parses the longest prefix that is a floating-point number. If no such prefix exists (e.g., in an empty string), NaN is returned.

Comparing parseFloat() and Number():

  • Applying parseFloat() to a nonstring is less efficient, because it coerces its argument to a string before parsing it. As a consequence, many values that Number() converts to actual numbers are converted to NaN by parseFloat():

    > parseFloat(true)  // same as parseFloat('true')
    > Number(true)
    > parseFloat(null)  // same as parseFloat('null')
    > Number(null)
  • parseFloat() parses the empty string as NaN:

    > parseFloat('')
    > Number('')
  • parseFloat() parses until the last legal character, meaning you get a result where you may not want one:

    > parseFloat('123.45#')
    > Number('123.45#')
  • parseFloat() ignores leading whitespace and stops before illegal characters (which include whitespace):

    > parseFloat('\t\v\r12.34\n ')

    Number() ignores both leading and trailing whitespace (but other illegal characters lead to NaN).

Special Number Values

JavaScript has several special number values:

  • Two error values, NaN and Infinity.
  • Two values for zero, +0 and -0. JavaScript has two zeros, a positive zero and a negative zero, because the sign and the magnitude of a number are stored separately. In most of this book, I pretend that there is only a single zero, and you almost never see in JavaScript that there are two of them.


The error value NaN (an abbreviation for “not a number”) is, ironically, a number value:

> typeof NaN

It is produced by errors such as the following:

  • A number could not be parsed:

    > Number('xyz')
    > Number(undefined)
  • An operation failed:

    > Math.acos(2)
    > Math.log(-1)
    > Math.sqrt(-1)
  • One of the operands is NaN (this ensures that, if an error occurs during a longer computation, you can see it in the final result):

    > NaN + 3
    > 25 / NaN

Pitfall: checking whether a value is NaN

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

> NaN === NaN

Strict equality (===) is also used by Array.prototype.indexOf. You therefore can’t search for NaN in an array via that method:

> [ NaN ].indexOf(NaN)

If you want to check whether a value is NaN, you have to use the global function isNaN():

> isNaN(NaN)
> isNaN(33)

However, isNaN does not work properly with nonnumbers, because it first converts those to numbers. That conversion can produce NaN and then the function incorrectly returns true:

> isNaN('xyz')

Thus, it is best to combine isNaN with a type check:

function myIsNaN(value) {
    return typeof value === 'number' && isNaN(value);

Alternatively, you can check whether the value is unequal to itself (as NaN is the only value with this trait). But that is less self-explanatory:

function myIsNaN(value) {
    return value !== value;

Note that this behavior is dictated by IEEE 754. As noted in Section 7.11, “Details of comparison predicates”:[15]

Every NaN shall compare unordered with everything, including itself.


Infinity is an error value indicating one of two problems: a number can’t be represented because its magnitude is too large, or a division by zero has happened.

Infinity is larger than any other number (except NaN). Similarly, -Infinity is smaller than any other number (except NaN). That makes them useful as default values—for example, when you are looking for a minimum or maximum.

Error: a number’s magnitude is too large

How large a number’s magnitude can become is determined by its internal representation (as discussed in The Internal Representation of Numbers), which is the arithmetic product of:

  • A mantissa (a binary number 1.f1f2...)
  • 2 to the power of an exponent

The exponent must be between (and excluding) −1023 and 1024. If the exponent is too small, the number becomes 0. If the exponent is too large, it becomes Infinity. 21023 can still be represented, but 21024 can’t:

> Math.pow(2, 1023)
> Math.pow(2, 1024)

Error: division by zero

Dividing by zero produces Infinity as an error value:

> 3 / 0
> 3 / -0

Computing with Infinity

You get the error result NaN if you try to “neutralize” one Infinity with another one:

> Infinity - Infinity
> Infinity / Infinity

If you try to go beyond Infinity, you still get Infinity:

> Infinity + Infinity
> Infinity * Infinity

Checking for Infinity

Strict and lenient equality work fine for Infinity:

> var x = Infinity;
> x === Infinity

Additionally, the global function isFinite() allows you to check whether a value is an actual number (neither infinite nor NaN):

> isFinite(5)
> isFinite(Infinity)
> isFinite(NaN)

Two Zeros

Because JavaScript’s numbers keep magnitude and sign separate, each nonnegative number has a negative, including 0.

The rationale for this is that whenever you represent a number digitally, it can become so small that it is indistinguishable from 0, because the encoding is not precise enough to represent the difference. Then a signed zero allows you to record “from which direction” you approached zero; that is, what sign the number had before it was considered zero. Wikipedia nicely sums up the pros and cons of signed zeros:

It is claimed that the inclusion of signed zero in IEEE 754 makes it much easier to achieve numerical accuracy in some critical problems, in particular when computing with complex elementary functions. On the other hand, the concept of signed zero runs contrary to the general assumption made in most mathematical fields (and in most mathematics courses) that negative zero is the same thing as zero. Representations that allow negative zero can be a source of errors in programs, as software developers do not realize (or may forget) that, while the two zero representations behave as equal under numeric comparisons, they are different bit patterns and yield different results in some operations.

Best practice: pretend there’s only one zero

JavaScript goes to great lengths to hide the fact that there are two zeros. Given that it normally doesn’t matter that they are different, it is recommended that you play along with the illusion of the single zero. Let’s examine how that illusion is maintained.

In JavaScript, you normally write 0, which means +0. But -0 is also displayed as simply 0. This is what you see when you use a browser command line or the Node.js REPL:

> -0

That is because the standard toString() method converts both zeros to the same '0':

> (-0).toString()
> (+0).toString()

Equality doesn’t distinguish zeros, either. Not even ===:

> +0 === -0

Array.prototype.indexOf uses === to search for elements, maintaining the illusion:

> [ -0, +0 ].indexOf(+0)
> [ +0, -0 ].indexOf(-0)

The ordering operators also consider the zeros to be equal:

> -0 < +0
> +0 < -0

Distinguishing the two zeros

How can you actually observe that the two zeros are different? You can divide by zero (-Infinity and +Infinity can be distinguished by ===):

> 3 / -0
> 3 / +0

Another way to perform the division by zero is via Math.pow() (see Numerical Functions):

> Math.pow(-0, -1)
> Math.pow(+0, -1)

Math.atan2() (see Trigonometric Functions) also reveals that the zeros are different:

> Math.atan2(-0, -1)
> Math.atan2(+0, -1)

The canonical way of telling the two zeros apart is the division by zero. Therefore, a function for detecting negative zeros would look like this:

function isNegativeZero(x) {
    return x === 0 && (1/x < 0);

Here is the function in use:

> isNegativeZero(0)
> isNegativeZero(-0)
> isNegativeZero(33)

The Internal Representation of Numbers

JavaScript numbers have 64-bit precision, which is also called double precision (type double in some programming languages). The internal representation is based on the IEEE 754 standard. The 64 bits are distributed between a number’s sign, exponent, and fraction as follows:

Sign Exponent ∈ [−1023, 1024] Fraction

1 bit

11 bits

52 bits

Bit 63

Bits 62–52

Bits 51–0

The value of a number is computed by the following formula:

(–1)sign × %1.fraction × 2exponent

The prefixed percentage sign (%) means that the number in the middle is written in binary notation: a 1, followed by a binary point, followed by a binary fraction—namely the binary digits of the fraction (a natural number). Here are some examples of this representation:


(sign = 0, fraction = 0, exponent = −1023)


(sign = 1, fraction = 0, exponent = −1023)


= (−1)0 × %1.0 × 20

(sign = 0, fraction = 0, exponent = 0)


= (−1)0 × %1.0 × 21


= (−1)0 × %1.1 × 21

(sign = 0, fraction = 251, exponent = 0)


= (−1)0 × %1.0 × 2−1


= (−1)1 × %1.0 × 20

The encodings of +0, −0, and 3 can be explained as follows:

  • ±0: Given that the fraction is always prefixed by a 1, it’s impossible to represent 0 with it. Hence, JavaScript encodes a zero via the fraction 0 and the special exponent −1023. The sign can be either positive or negative, meaning that JavaScript has two zeros (see Two Zeros).
  • 3: Bit 51 is the most significant (highest) bit of the fraction. That bit is 1.

Special Exponents

The previously mentioned representation of numbers is called normalized. In that case, the exponent e is in the range −1023 < e < 1024 (excluding lower and upper bounds). −1023 and 1024 are special exponents:

  • 1024 is used for error values such as NaN and Infinity.
  • −1023 is used for:

    • Zero (if the fraction is 0, as just explained)
    • Small numbers close to zero (if the fraction is not 0).

    To enable both applications, a different, so-called denormalized, representation is used:

    (–1)sign × %0.fraction × 2–1022

    To compare, the smallest (as in “closest to zero”) numbers in normalized representation are:

    (–1)sign × %1.fraction × 2–1022

    Denormalized numbers are smaller, because there is no leading digit 1.

Handling Rounding Errors

JavaScript’s numbers are usually entered as decimal floating-point numbers, but they are internally represented as binary floating-point numbers. That leads to imprecision. To understand why, let’s forget JavaScript’s internal storage format and take a general look at what fractions can be well represented by decimal floating-point numbers and by binary floating-point numbers. In the decimal system, all fractions are a mantissa m divided by a power of 10:

So, in the denominator, there are only tens. That’s why cannot be expressed precisely as a decimal floating-point number—there is no way to get a 3 into the denominator. Binary floating-point numbers only have twos in the denominator. Let’s examine which decimal floating-point numbers can be represented well as binary and which can’t. If there are only twos in the denominator, the decimal number can be represented:

  • 0.5dec = = = 0.1bin
  • 0.75dec = = = 0.11bin
  • 0.125dec = = = 0.001bin

Other fractions cannot be represented precisely, because they have numbers other than 2 in the denominator (after prime factorization):

  • 0.1dec = =
  • 0.2dec = =

You can’t normally see that JavaScript doesn’t store exactly 0.1 internally. But you can make it visible by multiplying it with a high enough power of 10:

> 0.1 * Math.pow(10, 24)

And if you add two imprecisely represented numbers, the result is sometimes imprecise enough that the imprecision becomes visible:

> 0.1 + 0.2

Another example:

> 0.1 + 1 - 1

Due to rounding errors, as a best practice you should not compare nonintegers directly. Instead, take an upper bound for rounding errors into consideration. Such an upper bound is called a machine epsilon. The standard epsilon value for double precision is 2−53:

var EPSILON = Math.pow(2, -53);
function epsEqu(x, y) {
    return Math.abs(x - y) < EPSILON;

epsEqu() ensures correct results where a normal comparison would be inadequate:

> 0.1 + 0.2 === 0.3
> epsEqu(0.1+0.2, 0.3)

Integers in JavaScript

As mentioned before, JavaScript has only floating-point numbers. Integers appear internally in two ways. First, most JavaScript engines store a small enough number without a decimal fraction as an integer (with, for example, 31 bits) and maintain that representation as long as possible. They have to switch back to a floating-point representation if a number’s magnitude grows too large or if a decimal fraction appears.

Second, the ECMAScript specification has integer operators: namely, all of the bitwise operators. Those operators convert their operands to 32-bit integers and return 32-bit integers. For the specification, integer only means that the numbers don’t have a decimal fraction, and 32-bit means that they are within a certain range. For engines, 32-bit integer means that an actual integer (non-floating-point) representation can usually be introduced or maintained.

Ranges of Integers

Internally, the following ranges of integers are important in JavaScript:

  • Safe integers (see Safe Integers), the largest practically usable range of integers that JavaScript supports:

    • 53 bits plus a sign, range (−253, 253)
  • Array indices (see Array Indices):

    • 32 bits, unsigned
    • Maximum length: 232−1
    • Range of indices: [0, 232−1) (excluding the maximum length!)
  • Bitwise operands (see Bitwise Operators):

    • Unsigned right shift operator (>>>): 32 bits, unsigned, range [0, 232)
    • All other bitwise operators: 32 bits, including a sign, range [−231, 231)
  • “Char codes,” UTF-16 code units as numbers:

Representing Integers as Floating-Point Numbers

JavaScript can only handle integer values up to a magnitude of 53 bits (the 52 bits of the fraction plus 1 indirect bit, via the exponent; see The Internal Representation of Numbers for details).

The following table explains how JavaScript represents 53-bit integers as floating-point numbers:

Bits Range Encoding

1 bit


(See The Internal Representation of Numbers.)

1 bit


%1 × 20

2 bits


%1.f51 × 21

3 bits

4–7 = 22–(23−1)

%1.f51f50 × 22

4 bits


%1.f51f50f49 × 23

53 bits


%1.f51⋯f0 × 252

There is no fixed sequence of bits that represents the integer. Instead, the mantissa %1.f is shifted by the exponent, so that the leading digit 1 is in the right place. In a way, the exponent counts the number of digits of the fraction that are in active use (the remaining digits are 0). That means that for 2 bits, we use one digit of the fraction and for 53 bits, we use all digits of the fraction. Additionally, we can represent 253 as %1.0 × 253, but we get problems with higher numbers:

Bits Range Encoding

54 bits


%1.f51⋯f00 × 253

55 bits


%1.f51⋯f000 × 254

For 54 bits, the least significant digit is always 0, for 55 bits the two least significant digits are always 0, and so on. That means that for 54 bits, we can only represent every second number, for 55 bits only every fourth number, and so on. For example:

> Math.pow(2, 53) - 1  // OK
> Math.pow(2, 53)  // OK
> Math.pow(2, 53) + 1  // can't be represented
> Math.pow(2, 53) + 2  // OK

Safe Integers

JavaScript can only safely represent integers i in the range −253 < i < 253. This section examines what that means and what the consequences are. It is based on an email by Mark S. Miller to the es-discuss mailing list.

The idea of a safe integer centers on 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 represent only every second mathematical integer (the previous section explains why). Therefore, a safe JavaScript integer is one that unambiguously represents a single mathematical integer.

Definitions in ECMAScript 6

ECMAScript 6 will provide the following constants:

Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;

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 an integer. If both checks succeed, n is safe if it is greater than or equal to MIN_SAFE_INTEGER and less than or equal to MAX_SAFE_INTEGER.

Safe results of arithmetic computations

How can we make sure that results of arithmetic computations are correct? For example, the following result is clearly not correct:

> 9007199254740990 + 3

We have two safe operands, but an unsafe result:

> Number.isSafeInteger(9007199254740990)
> Number.isSafeInteger(3)
> Number.isSafeInteger(9007199254740992)

The following result is also incorrect:

> 9007199254740995 - 10

This time, the result is safe, but one of the operands isn’t:

> Number.isSafeInteger(9007199254740995)
> Number.isSafeInteger(10)
> Number.isSafeInteger(9007199254740986)

Therefore, the result of applying an integer operator op is guaranteed to be correct only 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.

Converting to Integer

In JavaScript, all numbers are floating point. Integers are floating-point numbers without a fraction. Converting a number n to an integer means finding the integer that is “closest” to n (where the meaning of “closest” depends on how you convert). You have several options for performing this conversion:

  1. The Math functions Math.floor(), Math.ceil(), and Math.round() (see Integers via Math.floor(), Math.ceil(), and Math.round())
  2. The custom function ToInteger() (see Integers via the Custom Function ToInteger())
  3. Binary bitwise operators (see 32-bit Integers via Bitwise Operators)
  4. The global function parseInt() (see Integers via parseInt())

Spoiler: #1 is usually the best choice, #2 and #3 have niche applications, and #4 is OK for parsing strings, but not for converting numbers to integers.

Integers via Math.floor(), Math.ceil(), and Math.round()

The following three functions are usually the best way of converting a number to an integer:

  • Math.floor() converts its argument to the closest lower integer:

    > Math.floor(3.8)
    > Math.floor(-3.8)
  • Math.ceil() converts its argument to the closest higher integer:

    > Math.ceil(3.2)
    > Math.ceil(-3.2)
  • Math.round() converts its argument to the closest integer:

    > Math.round(3.2)
    > Math.round(3.5)
    > Math.round(3.8)

    The result of rounding -3.5 may be surprising:

    > Math.round(-3.2)
    > Math.round(-3.5)
    > Math.round(-3.8)

    Therefore, Math.round(x) is the same as:

    Math.floor(x + 0.5)

Integers via the Custom Function ToInteger()

Another good option for converting any value to an integer is the internal ECMAScript operation ToInteger(), which removes the fraction of a floating-point number. If it was accessible in JavaScript, it would work like this:

> ToInteger(3.2)
> ToInteger(3.5)
> ToInteger(3.8)
> ToInteger(-3.2)
> ToInteger(-3.5)
> ToInteger(-3.8)

The ECMAScript specification defines the result of ToInteger(number) as:

sign(number) × floor(abs(number))

For what it does, this formula is relatively complicated because floor seeks the closest larger integer; if you want to remove the fraction of a negative integer, you have to seek the closest smaller integer. The following code implements the operation in JavaScript. We avoid the sign operation by using ceil if the number is negative:

function ToInteger(x) {
    x = Number(x);
    return x < 0 ? Math.ceil(x) : Math.floor(x);

Binary bitwise operators (see Binary Bitwise Operators) convert (at least) one of their operands to a 32-bit integer that is then manipulated to produce a result that is also a 32-bit integer. Therefore, if you choose the other operand appropriately, you get a fast way to convert an arbitrary number to a 32-bit integer (that is either signed or unsigned).

Bitwise Or (|)

If the mask, the second operand, is 0, you don’t change any bits and the result is the first operand, coerced to a signed 32-bit integer. This is the canonical way to execute this kind of coercion and is used, for example, by asm.js (refer back to Is JavaScript Fast Enough?):

// Convert x to a signed 32-bit integer
function ToInt32(x) {
    return x | 0;

ToInt32() removes the fraction and applies modulo 232:

> ToInt32(1.001)
> ToInt32(1.999)
> ToInt32(1)
> ToInt32(-1)
> ToInt32(Math.pow(2, 32)+1)
> ToInt32(Math.pow(2, 32)-1)

Shift operators

The same trick that worked for bitwise Or also works for shift operators: if you shift by zero bits, the result of a shift operation is the first operand, coerced to a 32-bit integer. Here are some examples of implementing operations of the ECMAScript specification via shift operators:

// Convert x to a signed 32-bit integer
function ToInt32(x) {
    return x << 0;

// Convert x to a signed 32-bit integer
function ToInt32(x) {
    return x >> 0;

// Convert x to an unsigned 32-bit integer
function ToUint32(x) {
    return x >>> 0;

Here is ToUint32() in action:

> ToUint32(-1)
> ToUint32(Math.pow(2, 32)-1)
> ToUint32(Math.pow(2, 32))

Integers via parseInt()

The parseInt() function:

parseInt(str, radix?)

parses the string str (nonstrings are coerced) as an integer. The function ignores leading whitespace and considers as many consecutive legal digits as it can find.

The radix

The range of the radix is 2 ≤ radix ≤ 36. It determines the base of the number to be parsed. If the radix is greater than 10, letters are used as digits (case-insensitively), in addition to 0–9.

If radix is missing, then it is assumed to be 10, except if str begins with “0x” or “0X,” in which case radix is set to 16 (hexadecimal):

> parseInt('0xA')

If radix is already 16, then the hexadecimal prefix is optional:

> parseInt('0xA', 16)
> parseInt('A', 16)

So far I have described the behavior of parseInt() according to the ECMAScript specification. Additionally, some engines set the radix to 8 if str starts with a zero:

> parseInt('010')
> parseInt('0109')  // ignores digits ≥ 8

Thus, it is best to always explicitly state the radix, to always call parseInt() with two arguments.

Here are a few examples:

> parseInt('')
> parseInt('zz', 36)
> parseInt('   81', 10)

> parseInt('12**', 10)
> parseInt('12.34', 10)
> parseInt(12.34, 10)

Don’t use parseInt() to convert a number to an integer. The last example gives us hope that we might be able to use parseInt() for converting numbers to integers. Alas, here is an example where the conversion is incorrect:

> parseInt(1000000000000000000000.5, 10)


The argument is first converted to a string:

> String(1000000000000000000000.5)

parseInt doesn’t consider “e” to be an integer digit and thus stops parsing after the 1. Here’s another example:

> parseInt(0.0000008, 10)
> String(0.0000008)


parseInt() shouldn’t be used to convert numbers to integers: coercion to string is an unnecessary detour and even then, the result is not always correct.

parseInt() is useful for parsing strings, but you have to be aware that it stops at the first illegal digit. Parsing strings via Number() (see The Function Number) is less forgiving, but may produce nonintegers.

Arithmetic Operators

The following operators are available for numbers:

number1 + number2

Numerical addition, unless either of the operands is a string. Then both operands are converted to strings and concatenated (see The Plus Operator (+)):

> 3.1 + 4.3
> 4 + ' messages'
'4 messages'
number1 - number2
number1 * number2
number1 / number2
number1 % number2


> 9 % 7
> -9 % 7


This operation is not modulo. It returns a value whose sign is the same as the first operand (more details in a moment).

Negates its operand.
Leaves its operand as is; nonnumbers are converted to a number.
++variable, --variable

Returns the current value of the variable after incrementing (or decrementing) it by 1:

> var x = 3;
> ++x
> x
variable++, variable--

Increments (or decrements) the value of the variable by 1 and returns it:

> var x = 3;
> x++
> x

Mnemonic: increment (++) and decrement (--) operators

The position of the operand can help you remember whether it is returned before or after incrementing (or decrementing) it. If the operand comes before the increment operator, it is returned before incrementing it. If the operand comes after the operator, it is incremented and then returned. (The decrement operator works similarly.)

Bitwise Operators

JavaScript has several bitwise operators that work with 32-bit integers. That is, they convert their operands to 32-bit integers and produce a result that is a 32-bit integer. Use cases for these operators include processing binary protocols, special algorithms, etc.

Background Knowledge

This section explains a few concepts that will help you understand bitwise operators.

Binary complements

Two common ways of computing a binary complement (or inverse) of a binary number are:

Ones’ complement

You compute the ones’ complement ~x of a number x by inverting each of the 32 digits. Let’s illustrate the ones’ complement via four-digit numbers. The ones’ complement of 1100 is 0011. Adding a number to its ones’ complement results in a number whose digits are all 1:

1 + ~1 = 0001 + 1110 = 1111
Twos’ complement

The twos’ complement -x of a number x is the ones’ complement plus one. Adding a number to its twos’ complement results in 0 (ignoring overflow beyond the most significant digit). Here’s an example using four-digit numbers:

1 + -1 = 0001 + 1111 = 0000

Signed 32-bit integers

32-bit integers don’t have an explicit sign, but you can still encode negative numbers. For example, −1 can be encoded as the twos’ complement of 1: adding 1 to the result yields 0 (within 32 bits). The boundary between positive and negative numbers is fluid; 4294967295 (232−1) and −1 are the same integer here. But you have to decide on a sign when you convert such an integer from or to a JavaScript number, which has an explicit sign as opposed to an implicit one. Therefore, signed 32-bit integers are partitioned into two groups:

  • Highest bit is 0: number is zero or positive.
  • Highest bit is 1: number is negative.

The highest bit is often called the sign bit. Accordingly, 4294967295, interpreted as a signed 32-bit integer, becomes −1 when converted to a JavaScript number:

> ToInt32(4294967295)

ToInt32() is explained in 32-bit Integers via Bitwise Operators.


Only the unsigned right shift operator (>>>) works with unsigned 32-bit integers; all other bitwise operators work with signed 32-bit integers.

Inputting and outputting binary numbers

In the following examples, we work with binary numbers via the following two operations:

Bitwise Not Operator

~number computes the ones’ complement of number:

> (~parseInt('11111111111111111111111111111111', 2)).toString(2)

Binary Bitwise Operators

JavaScript has three binary bitwise operators:

  • number1 & number2 (bitwise And):

    > (parseInt('11001010', 2) & parseInt('1111', 2)).toString(2)
  • number1 | number2 (bitwise Or):

    > (parseInt('11001010', 2) | parseInt('1111', 2)).toString(2)
  • number1 ^ number2 (bitwise Xor; eXclusive Or):

    > (parseInt('11001010', 2) ^ parseInt('1111', 2)).toString(2)

There are two ways to intuitively understand binary bitwise operators:

One boolean operation per bit

In the following formulas, ni means bit i of number n interpreted as a boolean (0 is false, 1 is true). For example, 20 is false; 21 is true:

  • And: resulti = number1i && number2i
  • Or: resulti = number1i || number2i
  • Xor: resulti = number1i ^^ number2i

    The operator ^^ does not exist. If it did, it would work like this (the result is true if exactly one of the operands is true):

    x ^^ y === (x && !y) || (!x && y)
Changing bits of number1 via number2
  • And: Keeps only those bits of number1 that are set in number2. This operation is also called masking, with number2 being the mask.
  • Or: Sets all bits of number1 that are set in number2 and keeps all other bits unchanged.
  • Xor: Inverts all bits of number1 that are set in number2 and keeps all other bits unchanged.

Bitwise Shift Operators

JavaScript has three bitwise shift operators:

The Function Number

The function Number can be invoked in two ways:


As a normal function, it converts value to a primitive number (see Converting to Number):

> Number('123')
> typeof Number(3)  // no change
new Number(num)

As a constructor, it creates a new instance of Number (see Wrapper Objects for Primitives), an object that wraps num (after converting it to a number). For example:

> typeof new Number(3)

The former invocation is the common one.

Number Constructor Properties

The object Number has the following properties:


The largest positive number that can be represented. Internally, all digits of its fraction are ones and the exponent is maximal, at 1023. If you try to increment the exponent by multiplying it by two, the result is the error value Infinity (see Infinity):

> Number.MAX_VALUE
> Number.MAX_VALUE * 2

The smallest representable positive number (greater than zero, a tiny fraction):

> Number.MIN_VALUE
The same value as the global NaN.

The same value as -Infinity:

> Number.NEGATIVE_INFINITY === -Infinity

The same value as Infinity:

> Number.POSITIVE_INFINITY === Infinity

Number Prototype Methods

All methods of primitive numbers are stored in Number.prototype (see Primitives Borrow Their Methods from Wrappers).


Number.prototype.toFixed(fractionDigits?) returns an exponent-free representation of the number, rounded to fractionDigits digits. If the parameter is omitted, the value 0 is used:

> 0.0000003.toFixed(10)
> 0.0000003.toString()

If the number is greater than or equal to 1021, then this method works the same as toString(). You get a number in exponential notation:

> 1234567890123456789012..toFixed()
> 1234567890123456789012..toString()


Number.prototype.toPrecision(precision?) prunes the mantissa to precision digits before using a conversion algorithm similar to toString(). If no precision is given, toString() is used directly:

> 1234..toPrecision(3)

> 1234..toPrecision(4)

> 1234..toPrecision(5)

> 1.234.toPrecision(3)

You need the exponential notation to display 1234 with a precision of three digits.


For Number.prototype.toString(radix?), the parameter radix indicates the base of the system in which the number is to be displayed. The most common radices are 10 (decimal), 2 (binary), and 16 (hexadecimal):

> 15..toString(2)
> 65535..toString(16)

The radix must be at least 2 and at most 36. Any radix greater than 10 leads to alphabetical characters being used as digits, which explains the maximum 36, as the Latin alphabet has 26 characters:

> 1234567890..toString(36)

The global function parseInt (see Integers via parseInt()) allows you to convert such notations back to a number:

> parseInt('kf12oi', 36)

Decimal exponential notation

For the radix 10, toString() uses exponential notation (with a single digit before the decimal point) in two cases. First, if there are more than 21 digits before the decimal point of a number:

> 1234567890123456789012
> 123456789012345678901

Second, if a number starts with 0. followed by more than five zeros and a non-zero digit:

> 0.0000003
> 0.000003

In all other cases, a fixed notation is used.


Number.prototype.toExponential(fractionDigits?) forces a number to be expressed in exponential notation. fractionDigits is a number between 0 and 20 that determines how many digits should be shown after the decimal point. If it is omitted, then as many significant digits are included as necessary to uniquely specify the number.

In this example, we force more precision when toString() would also use exponential notation. Results are mixed, because we reach the limits of the precision that can be achieved when converting binary numbers to a decimal notation:

> 1234567890123456789012..toString()

> 1234567890123456789012..toExponential(20)

In this example, the magnitude of the number is not large enough for an exponent being displayed by toString(). However, toExponential() does display an exponent:

> 1234..toString()

> 1234..toExponential(5)

> 1234..toExponential()

In this example, we get exponential notation when the fraction is not small enough:

> 0.003.toString()

> 0.003.toExponential(4)

> 0.003.toExponential()

Sources for This Chapter

I referred to the following sources while writing this chapter:

[14] Source: Brendan Eich,

[15] Béla Varga (@netzzwerg) pointed out that IEEE 754 specifies NaN as not equal to itself.

Next: 12. Strings