What is NaN in JavaScript?

What is NaN in JavaScript?

This article is a part of the ReturnTrue series. Here we solve ReturnTrue puzzles and try to explain how we find solutions and why exactly they work.

The goal is not to show the answers but to dive deep into the elaborate internals of JavaScript and browsers. Yet, we recommend you try to find solutions yourself. It would be more interesting this way, be sure.

ReturnTrue series:

  1. A useful function that does nothing

  2. What is NaN in JavaScript?

Now let’s continue our journey and move to the 2nd task and the main topic of this article — NaN.

Reflexive

function reflexive(x) {
  return x != x;
}

If you are an experienced developer, you know the answer:

reflexive(NaN)

But there is something to discuss.

First, why is it “reflexive”?

In a nutshell, a binary relation across a set is reflexive if each element of the set is related to itself.

Math definitions from Wikipedia, brrr. Let’s explain it another way.

Imagine a set of natural numbers. You can take any number and check whether it equals itself or not:

> 1 == 1
true

If this check returns true for all the natural numbers, then equality is “reflexive” for them.

The same goes for inequality. Inequality is “anti-reflexive” because it’s false for all the natural numbers:

> 1 != 1
false

But NaN is not equal to itself. It means that for NaN equality is anti-reflexive, while inequality is reflexive. Hence the name.

> NaN == NaN
false

> NaN != NaN
true

Second, how to get NaN in JavaScript?

If we try to make a number from a non-numeric string, JavaScript will not throw an exception. Instead, it will return NaN. It is, well, understandable. But JavaScript is one of the few languages that returns NaN in such a common operation.

E.g. Python throws an exception:

int("a")
$ python nan.py
Traceback (most recent call last):
  File "./nan.py", line 1, in <module>
    int("a")
ValueError: invalid literal for int() with base 10: 'a'

PHP returns zero:

<?
$str = "a";
$num = (int)$str;
echo $num;
?>
$ php -f nan.php
0

But JavaScript says:

> Number('a')
NaN

That’s why every JavaScript newbie knows about NaN. Usually, they do not go further than "NaN is, eh, when a string to number conversion failed". But we will go deeper.

Going deeper

As you know, when parseInt can not do its job, it returns NaN:

> parseInt('a')
NaN

So, we have to check the result of such parsing for NaN. There is a global function isNaN, that is available since the first edition of ECMAScript. This function is suitable for this check, but you should not trust it all the time. isNaN tries to convert the passed argument to a number before the very check. That’s why isNaN returns true for any value that it can’t convert to a number:

> isNaN({})
true

With this in mind, the standard authors should have named isNaNcanNotBeConvertedToNumber. To fix this issue, they introduced Number.isNaN in ES6, which works as expected:

> Number.isNaN({})
false

Unclear browser API is not the only problem in JavaScript related to NaN. As you also may know, NaN as an operand transforms any arithmetic operation result into NaN:

> 1 + NaN
NaN

Plus, NaN does not equal anything, even itself:

> false == NaN
false

> NaN != NaN
true

Such behavior is so unique that the spec suggests checking NaN using a strict inequality operation. Like, duck testing:

A reliable way for ECMAScript code to test if a value X is a NaN is an expression of the form X !== X. The result will be true if and only if X is a NaN.

18.2.3 isNaN

It’s important to note here that this situation is not the same as we have with objects in JavaScript:

> {} !== {}
true

But in this case we’re comparing two different objects, while with NaN we’re comparing the same value. To make it clear, consider these examples:

> const obj = {}
> obj !== obj
false
> const nan = NaN
> NaN !== NaN
true

Anyway, aside from those described above, there are plenty of other ways to get NaN in JavaScript. Most of them are not related to arithmetic operations at all. E.g., you can try to get a code of the character of the string outside of the length of this string:

> ''.charCodeAt(1)
NaN

Or parse an invalid date string:

> Date.parse('a')
NaN

From some perspective, the fact that we get NaN in such cases is okay, but is it? If you can’t get a code of the character of the string, you would like to get an exception signaling the error. But in JavaScript, access to any non-existing element of an array does not lead to an error:

> [1, 2][3]
undefined

> ({})['a']
undefined

> ''[0]
undefined

That’s why in our case JavaScript tries to return a “thing”, which is a number and an error at the same time. You know, NaN is a number:

> typeof NaN
"number"

Shameless plug you may want to click on:

Look at this


Going even deeper

The NaN weirdness in JavaScript does not end here.

If we try to get the NaN-th character of the non-empty string, we’ll get it:

> 'a'.charAt(NaN)
"a"

It works this way because sometimes JS converts NaN to zero behind the scenes. Surprise!

There are lots of different type conversions described in the spec. One of them is ToInteger. JavaScript uses it when a standard function expects an integer argument. Before the use of the passed argument, JavaScript applies ToInteger to the value of that argument. If the value is NaN, then ToInteger replaces it with zero:

2. If number is NaN, +0, or -0, return +0.

7.1.5 ToInteger

Aside from that, there are more curious cases with NaN. Check this out:

> [1, 2, NaN].indexOf(NaN)
-1

> [1, 2, NaN].includes(NaN)
true

indexOf can’t find NaN, while includes can. Why? Because indexOf uses strict equality checks during the search. NaN does not equal itself, so indexOf can’t find it.

includes search is a bit more complicated. It has an explicit check for NaN for the argument and array values.

Another example is exponentiation which goes nuts when we pass NaN. Yeah, we know that “NaN turns any arithmetic operation into NaN” but for the sake of God, what is going on here?

> NaN ** 0
1

It’s a tricky question. Exponentiation in JavaScript does not care what exactly you are trying to raise to the power 0. The result will always be the same:

> ({ wtf: true }) ** 0
1

> (function orly() {}) ** 0
1

Honestly, the exponentiation algorithm is not that simple at all:

> (-10) ** 1.2
NaN

Here we get NaN not because of JavaScript weirdness, but that’s the story for the next time.

Wait, but what is NaN, once again?

Oh, glad you asked!

JavaScript implementations are trying to be ECMAScript compliant, right? Let’s check what the spec says about NaN:

Number value that is a IEEE 754 “Not-a-Number” value.

4.3.24 NaN

sigh

Alright, what is that IEEE 754 thing?

IEEE stands for “Institute of Electrical and Electronics Engineers”. It is a professional association that develops engineering standards. In particular, they create standards for telecommunications.

There are IEEE standards for the many things we use daily. E.g. IEEE 1003 is POSIX, while IEEE 802 is a set of standards describing different networks. In particular, 802.11 describes wireless communications, in other words — Wi-Fi. Hence the “versions” of Wi-Fi: 802.11a, 802.11b, and so on. All of them are different versions of that standard.

So, IEEE 754 is a standard for floating-point arithmetic. It describes the implementation of floating point numbers and operations for them what those numbers are we will see in the next articles

Among other things, the standard defines NaN as a special numeric value. Computers should return NaN when they can’t find the operation result. For example, when it’s impossible to compute or the operation itself is invalid.

There are lots of examples of such operations. There are the ones the standard describes:

  • 0 by 0 or ∞ by ∞ division;

  • 0 by ∞ multiplication;

  • getting a reminder of ∞ by 0 division;

  • subtraction of infinities of the same sign;

  • and so on.

There is NaN in most programming languages because they follow the standard. But not all the operations listed above return NaN in those languages. Some may throw exceptions if the language developers have decided so. Otherwise, NaN is usually used.

For instance, you can not divide by zero in Python:

0/0
$ python zero.py
Traceback (most recent call last):
  File "./zero.py", line 1, in <odule>
ZeroDivisionError: division by zero

But you can get NaN by trying to add +∞ to −∞:

float('inf') + float('-inf')
$ python inf.py
nan

Go will do the same, while in PHP you may get NaN by trying to get an arccosine of the value out of [−1; 1]:

<?
echo acos(2);
?>
$ php -f acos.php
NaN

The same is true for C.

As you see, programming languages work with NaN in varied ways. They may not even use NaN in arithmetic. They may throw exceptions in “critical” cases and return defaults in the others.

But some languages use NaN for all the awkward cases. JavaScript is one of those languages.

NaN and floating-point arithmetic are extensive and intriguing topics. But that’s all for today, folks!

If you wish to continue the journey, here is the cliffhanger. There is not one NaN for “all the weirdness”. IEEE 754 defines two of them: quiet NaN and signaling NaN. Happy research!

Originally published by Igor Adamenko in Uploadcare Blog