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;
}
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 isNaN
— canNotBeConvertedToNumber
. 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 aNaN
is an expression of the formX !== X
. The result will betrue
if and only ifX
is aNaN
.
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:
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
.
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.
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
Top comments (1)
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: