Understanding NaN Values
A co-worker recently had issues with NaN
values in JavaScript. NaN
values are IEEE 754 Floating-Point number values. These values result from an undefined or unrepresentable value in floating point calculations. NaN
values are number values despite everything your brain might tell you when you read NaN
as ‘Not a Number’. In the case of NaN
values it’s useful to forget all logic and intuition, and memorize the facts.
JavaScript reports NaN
values as number types, this is correct per the ECMA-262 and IEEE 754 specs.
In JavaScript a NaN
value is the only type of value that is not reflexive. That’s one way to identify a NaN
value. x !== x
where x
is a NaN
value will evaluate to true
. The global isNaN
method can be used to identify NaN
values. However, the isNaN
method doesn’t handle converting input to a number. It has a set of gotchas.
When certain inputs are passed to the isNaN
method the behavior is less than ideal. Several false positives are returned. Both Underscore
and Lo-Dash
handle the shortcomings of the isNaN
method by checking if the value is a number.
As written above NaN
values are number values so _.isNumber(NaN)
correctly returns true
for both Underscore
and Lo-Dash
. Both libraries have had issues filed for incorrect behavior, but the behavior is correct and the answer is usually to point consumers to the _.isFinite
method.
Issues also arise when dealing with builtin methods such as Array.prototype.indexOf
. NaN
values are unable to be found without special handling.
The ES6 draft specifies a solution to the global isNaN
method with a new Number.isNaN
method. Number.isNaN
returns the same results Underscore
and Lo-Dash
return.
The ES6 draft also specifies Object.is
, offering a better way to determine if two values are equal over the strict equality operator, triple equals. Object.is
works as the strict equality operator with two exceptions: NaN
values, and positive and negative zero.
When my co-worker was having issues and asking question about NaN
values he called ‘wat’ on JavaScript. I took it upon myself to set the record straight, but realized everyone who encounters NaN
values must learn about these problems, hence this post. It’s important to understand this behavior is not specific to JavaScript. It is specific to all languages that use IEEE 754 Floating-Point number calculations.
Ruby has the following behavior with regards to NaN
values.
Java has the following behavior with regards to NaN
values.
The behavior across Ruby, Java, and JavaScript is essentially the same. Every single consumer of these languages must eventually learn the rules of NaN
values. I hope consumers of these languages are now able to understand the issues at hand and deal with them thoughtfully.