From 0 to 9: The Magic of Numbers in JavaScript

Published Sep 07, 2016Last updated Jan 18, 2017
From 0 to 9: The Magic of Numbers in JavaScript

Numbers have a hidden power. Sometimes they are used in powerful forms of sorcery—just like in JavaScript. And as long you use them the right way, numbers can be a source of lot of fun in learning JS.

🤓 Note: I'm going to use ES2015 in the following examples. Not all will work
natively everywhere (you need a ES2015 supported interpreter, but don't worry probably you don't have to take care of that; you probably have it already).

Some of the examples are ES2015 specific. In that case, there is a specific note.

So, let's go!

Learning JavaScript by the Numbers—from 0 to 9

0. Zero-based things

Like in other programming languages, array indexes are zero-based. You start counting with zero; why is this so?

First: Whoops, I'll try again.

Zero: It's about saving space. The elements from any array are stored somewhere in the memory.

Imagine the memory looks like this:

Memory: _  _  _  _  _  _  _  _  _  _  _  _  _  
Data:                       [A, B, C]
                             ^ First index

Now, let's assume the array is located in memory at location X. In zero-based indexing, to access the element at index i (by using array[i]), the computer has to: get me the content at location X + i (e.g. for i = 0, array[X + 0] will be the first element).

In case we want one-based indexing, we lose one position in the memory buffer:

Memory: _  _  _  _  _  _  _  _  _  _  _  _  _  
Data:                       [   A, B, C]
                             ^ First index
                             ^ Unused memory gap

So the location X is now not used at all, while the next ones are used. If you want to use it, then you need to do a subtraction (get me content at X + i - 1), which is explained in the next point.

First: It's about optimizing things. Starting the count from zero is more efficient.
Let's take an example:

let fruits = ["Apples", "Pears", "Oranges"];
// Indexes:  0               1             2

The length of the array is 3 (there are three elements). We can say that any index number is matching in the following math expression:

0 ≤ index < 3

0 ≤ 0 < 3 → true
0 ≤ 1 < 3 → true
0 ≤ 2 < 3 → true

Now let's see what would happen if we have a one-based indexing:

let fruits = ["Apples", "Pears", "Oranges"];
// Indexes:  1           2        3
let N = fruits.length;

1 ≤ index < N + 1

1 ≤ 1 < 4
1 ≤ 2 < 4
1 ≤ 3 < 4

You may think now: well, what's the issue here? The problem is when we do N + 1. It's a bit of unnecessary extra work for the computer.

Second: 1 is the smallest integer which is still a valid value for a non-empty array length.


🤓 Note: The months in JavaScript dates are also zero-based (0-11). If you want to get the first of January 2016, you want to do:

console.log(new Date(2016, 0, 1).toString())
→ Fri Jan 01 2016 00:00:00 GMT+0200 (EET)

Could that be because months have labels attached with the indexes, so it's faster to access them internally (like I mentioned above)? Maybe!

1. Representing numbers and working with them

There are many ways to represent numbers in JavaScript. Here are a few of them. In some cases, the syntax looks weird and even though it's a valid syntax, you probably won't use it. But it's good to know that such cases are possible.

Integers

> 42
42

// The dot is displayed only if there are non-zero digits after the dot
> 42.000
42

Floating point

> 3.14
3.14

Octals (aka base 8)

Octals use a leading 0:

> 0123
83

Note the above format is forbidden in ES5 strict mode. In ES6, we can use 0o...:

> 0o123
83

Hex (aka base 16)

> 0xFF
255

> parseInt("0xFF", 16)
255

Exponent

eX is an abbreviation for multiply with 10ˣ.

> 7e3
7000

Explanation:

7 * 10 * 10 * 10
    \__________/
7 *    (1)e3
7 *    1000
7000

💡 Tip: It works nicely with negative exponents:

> 7e-2
0.07

Binary (added in ES2015+)

It starts with 0b, followed by the binary snippet (1 and 0 digits).

> 0b1101
13

Playing with the dot:

> 42.
42

For values lower than 0, you can start directly with the dot:

> .42
0.42

💡 Tip: This works in CSS as well:

div.myElement {
   opacity: .5;
}

2. Playing with the Number constructor

All the numbers above have the Number constructor:

> a = 42
42
> a.constructor
[Function: Number]
> typeof a
"number"

So, obviously they are numbers. But one thing we should remember is that in JavaScript, everything is an object. So, are numbers objects in JavaScript? Of course!

A simple proof is by adding a new method to the Number prototype:

Number.prototype.me = function () {
    return this;
};

// Let's create a variable
let foo = 42;

// Obviously, it's a number
typeof foo;
// → "number"

// But let's use the `me` method which we added
// (it returns the Number instance)
typeof foo.me();
// → "object"

// And of course it can be used in operations
foo.me() + 1;
// → 43

We can create such numbers using new Number(...).

let a = new Number(42);
typeof a;
// → "object"

a + 1;
// → 43

// Note, by outputting this, we get an object:
console.log(a);
// → [Number: 42]

// Calling the valueOf will return the real number
console.log(a.valueOf());
// → 42

Is this actually useful for anything? Well, yes!

The Number constructor can be used without new as well. It will convert the things into numbers (read more about this here):

let str = "\n\n42\t\n";
let foo = Number(str);
console.log(foo);
// → 42

Putting these two things together, we can create our own object, and return set a custom conversion when we use it in Number operations:

let magicObject = {
    foo: 42
  , hello: "world"
    // This will return the numeric value 
  , valueOf () {
       return this.foo;
    }
};


// Let's convert it into a number
console.log(+magicObject);
// → 43

console.log(-magicObject);
// → -43

// Add 1 to the sum
console.log(magicObject + 1);
// → 43

// Additionally, you can set a custom stringify function:
magicObject.toString = function () { return this.hello; };

console.log(`Hello ${magicObject}!`);
// → "Hello world!"

3. Invoking methods on number literals

The following syntax is strange, but still valid:

> .42.toString()
"0.42"

> 0.42.toString()
"0.42"

💡 Tip: It's a good practice to use wrapping parentheses around such numbers, and here's why:

> (0.42).toString()
"0.42"

> (.42).toString()
"0.42"

> .42.toString()
"0.42"

> 0.42.toString()
"0.42"

// Note this: we do call the toString() function,
// but get a number in the end
> -0.42.toString()
-0.42

// When adding the wrapping parentheses, it's working fine
> (-0.42).toString()
'-0.42'

// But if we take the operator outside of the parentheses,
// we do get a number
> -(0.42).toString()
-0.42

If you use an arithmetic operator there (generally -), you need to wrap it, too.
Otherwise, you'll get a number in the end. This is because of the operations order:

   -0.42.toString()
//  \_____________/
// -     '0.42'
// -0.42

When you wrap your numbers between parentheses, you simply convert the (negative) number into a string.

4. Strictly use strict equal! === !== == (AKA strict equal)

As mentioned in this article, it's definitely better to use strict equal.

If you know a bit of JavaScript, this is one of the
first things you learn. In most popular programming languages, to check if something equals something else, you're going to have to use ==:

// Assign the value
let foo = 42;

// Check if `foo` is 42
foo === 42
// → true

In JavaScript, this works but it does something else. It's a liberal equal, if you like to call it so:

"42" == 42
// → true

// Add some spaces, new lines and tabs in that string
"\n\t 42 \t\n\n" == 42
// → true

Using === (strict equal) definitely makes a change:

"42" === 42
// → false

5. Special numbers

There are quite a few special numbers:

  • -0—negative zero (usually we use the positive value: +0 or just 0)
  • NaN (not a number)
  • Infinity and -Infinity

In the Number object you will find quite a few other constants:

// The largest representable number
> Number.MAX_VALUE
1.7976931348623157e+308

// The smallest representable number
> Number.MIN_VALUE
5e-324

// Special "not a number" value
> Number.NaN
NaN

// Special negative infinite value; returned on overflow
> Number.NEGATIVE_INFINITY
-Infinity

// Special positive infinite value; returned on overflow
> Number.POSITIVE_INFINITY
Infinity

// Difference between one and the smallest value greater
// than one that can be represented as a Number.
> Number.EPSILON
2.220446049250313e-16

// Minimum safe integer in JavaScript.
> Number.MIN_SAFE_INTEGER
-9007199254740991

// Maximum safe integer in JavaScript.
> Number.MAX_SAFE_INTEGER
9007199254740991

Also, there are few constants in the Math object (e.g. Math.PI, Math.E etc).

6. Catching -0

Note that -0 === +0 is true. But still, they are doing totally different things:

> 42 / 0
Infinity
> 42 / -0
-Infinity

So, if you ever have to check if it's a negative zero, you can simply make such a division and see what you get.

7. Weird results and expressions

As mentioned above, we do get (negative) Infinity values when dividing most of the numbers to (negative) zero.

But still, there are few other cases when we get interesting results:

// Dividing zero by zero
0 / 0
→ NaN

// Note there is no -NaN
0 / -0
→ NaN

A rather nice one is 0⁰ which should be mathematically impossible—at least that's what they teach us in school. Well, JavaScript says it's 1, and that's dictated by specs (if the exponent is 0, the result is 1 always).

Math.pow(0, 0)
→ 1

Math.pow(NaN, 0);
→ 1

Infinity plus anything is still infinity:

Infinity + 42
→ Infinity

Infinity + Infinity
→ Infinity

Though, you get a NaN if you subtract Infinity from itself:

Infinity - Infinity
→ NaN

You may be surprised that in JavaScript, 0.1 + 0.2 === 0.3 is false:

0.1 + 0.2 === 0.3
// → false
What's going on here?!

Well, it is due to errors in the floating point precision. Let's take a closer look:

> 0.1 + 0.2
0.30000000000000004

> 0.1 * 0.2
0.020000000000000004

But why is this happening?! It's because of the way computers represent the numbers internally. The short explanation would be: when you divide 1 / 3, you probably round it to 0.333.... But if you're going to sum 0.333... three times, you will get 0.999... instead of 1 (note in this case ... doesn't represent an infinite series of decimals, but just your precision; btw, X.999999...—an infinite series of 9—does equal X + 1 in the real world).

So, it's just because of precision. You'll find better and longer explanations here; and how to solve such problems.

Does null equal zero?

You'd say no: null === 0 → false, but let's look at this:

> null < 0
false
> null <= 0
true
> null === 0
false
> null == 0
false
> null >= 0
true
> null > 0
false

So, what's going on here, again?! Well, it's JavaScript.

As first hint, we can check how is null converted into a number: +null0. Since null converted to a number is 0, the expressions above make sense—except the strict equal one. The explanation is easy: all the others call the ToPrimitive operation with a Number as preferred type.

8. The NaN (not a number) thingy

NaN obviously means not a number. Ironically, NaN is a number (well, it has the number type):

typeof NaN // "number"
NaN.constructor // Number

There are multiple ways to assign NaN to a variable:

// Just by using the `NaN` value
let foo = NaN; // NaN

// By parsing a non-number as number
parseInt("foo") // NaN

// Or shorter:
+'foo' // NaN

Sometimes x !== x

Because IEEE 754 dictates that NaN !== NaN, there we are:

NaN !== NaN
→ true

That's the reason why you should never check if foo is a not a number by checking if foo === NaN but by checking if foo !== foo or isNaN(foo).

Do you want to create another similar value by doing the same thing?

Well, we can try something that's almost the same. Let's do it:

// Let's define a property having a getter which returns a random number always
Object.defineProperty(this, "foo", {
   get () { return Math.random(); }
});

foo
→ 0.5300470317035524

foo
→ 0.4810690031991778

...

// Now, of course that:
foo !== foo
→ true

However, if you create a variable and assign the foo's value to it, then it won't work anymore (but it does work with NaN):

> a = foo
0.2264466197115489
> a === a
true
> a = NaN
NaN
> a === a
false

9. The power of bitwise operators

There are quite a few amazing things you can do with the low level operators which modify the bits of the numbers.

Fast multiplications/divisions

Do you want to multiply/divide a number with powers of 2 (2, 4, 8, 16 etc)? Do you want to make it extremely fast? Then, the answer is to use the << operator:

// Same with 3 * 2, but faster
> 3 << 1
6

// Same with 3 * 4
> 3 << 2
12

// Same with 3 * 8
> 3 << 3
24

// Divide by two
> 16 >> 1
8

What happens is that << moves all the bits to the left one position. Since the numbers are represented in binary, it adds a new 0 on the right side:

// 5 in base 2:
101

101 << 1
→ 1010 (which is 10 in base 2)

Similar things happen when using the >> operator.

From my testing, using this << is about 1.04 times faster than using the * operator. Maybe it makes sense when you really care about performance (e.g. rendering some complicated 3D animations).

📝 Note: This doesn't work for non-integers.

Checking if an element is included into an array

Using the ~ (bitwise NOT) bitwise operator to find if an element is included into an array:

let myArray = [1, 2, 42];
if (~myArray.indexOf(42)) {
   /* do something */
}

What happens is that there is indexOf that returns the index of the element (in this case, 2), or -1 if the element doesn't exist. -1 is the only number for which ~ will return 0. ~ turns 0 into 1 and 1 into 0 at bit level.

~-1
→ 0

~0
→ 0

~42
→ -43

~-42
~41

Then, obviously, 0 is a false value when converted to boolean, while the other numbers returned by ~ are truly values. So, we can do something like this:

let myArray = [4, 3, 42, 1];

// Get the index:
myArray.indexOf(42)
// → 2

// Let's check if it exists (convert to boolean)
!!~myArray.indexOf(42)
→ true

// ... and one which doesn't exist:
!!~myArray.indexOf(7)
→ false

Cryptography fun

The XOR (^) operator is really cute. You can encrypt and decrypt messages using it. Here is how it works:

A   B   ^
=========
0   0   0
0   1   1
1   0   1
1   1   0

This is used in cryptography. A simple example would be encrypting and decrypting a number:

// Alice and Bob know the same secret key:
let key = 42;

// Alice wants to send Bob a number
let msg = 7;

// But before sending it, Alice encrypts it:
msg = msg ^ key // or directly: msg ^= key
→ 45

// Bob receives 45, but knowing the key is 42 he knows to decrypt it:
45 ^ key
→ 7

// Now Bob can enjoy the message from Alice

Wrapping up

Hopefully you enjoyed this read and you learned something! Have fun using numbers for the good of this world. 🚀

Discover and read more posts from Johnny B. (Ionică Bizău)
get started