DEV Community

Cover image for What is NaN in JavaScript?
Uploadcare
Uploadcare

Posted on • Originally published at uploadcare.com

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.

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;
}
Enter fullscreen mode Exit fullscreen mode

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

reflexive(NaN)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode
$ 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'
Enter fullscreen mode Exit fullscreen mode

PHP returns zero:

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

But JavaScript says:

> Number('a')
NaN
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Plus, NaN does not equal anything, even itself:

> false == NaN
false

> NaN != NaN
true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
> const nan = NaN
> NaN !== NaN
true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Or parse an invalid date string:

> Date.parse('a')
NaN
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

> (-10) ** 1.2
NaN
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
$ python zero.py
Traceback (most recent call last):
  File "./zero.py", line 1, in <odule>
ZeroDivisionError: division by zero
Enter fullscreen mode Exit fullscreen mode

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

float('inf') + float('-inf')
Enter fullscreen mode Exit fullscreen mode
$ python inf.py
nan
Enter fullscreen mode Exit fullscreen mode

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);
?>
Enter fullscreen mode Exit fullscreen mode
$ php -f acos.php
NaN
Enter fullscreen mode Exit fullscreen mode

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

Top comments (1)

Collapse
 
uploadcare profile image
Uploadcare

Proud to say that this article triggered Brendan Eich, the creator of JavaScript, to explain some JS design decisions like null == undefined, NaN everywhere, and so on.

Feel free to follow the thread and join the discussion: