What Is NaN: A Deeper Dive Into JavaScript's Implementation of the IEEE-754 Floating Point Standard

Updated on · 6 min read
What Is NaN: A Deeper Dive Into JavaScript's Implementation of the IEEE-754 Floating Point Standard

NaN, short for "Not a Number", is a special value in JavaScript and TypeScript (as well as many other programming languages) that represents an undefined or unrepresentable value resulting from an arithmetic operation. It is often used as a placeholder for a missing numerical value or as a way to signify an error.

However, what exactly is the type of NaN in JavaScript? Is it a number, or is it a distinct data type? In this article, we will explore the intricacies of the NaN data type, including its behavior in arithmetic operations, its relationship with other data types in JavaScript and beyond, and the results of the typeof operator when applied to NaN. We will also briefly discuss the usage of NaN in TypeScript, as well as the built-in Number.isNaN() and isNaN() functions that can be used to test for the NaN data type.

NaN data type in JavaScript

When exploring the NaN data type in JavaScript, you may encounter a seemingly peculiar behavior. By typing typeof NaN into your browser console, the result displayed is number. This may initially appear to be an odd quirk of the JavaScript programming language. However, it is actually a standard feature of all programming languages that implement The IEEE Standard for Floating-Point Arithmetic (IEEE 754).

TypeScript, for example, doesn't have a separate type to signify NaN values, therefore this code is valid:

ts
let myNumber: number = NaN;
ts
let myNumber: number = NaN;

Moreover, TypeScript doesn't have a special mechanism to check for NaN values. To check if a value is NaN in TypeScript, you can use the Number.isNaN() or isNaN() functions, discussed in more detail below.

ts
let myNumber: number = NaN; if (Number.isNaN(myNumber)) { console.log("myNumber is NaN"); } else { console.log("myNumber is not NaN"); }
ts
let myNumber: number = NaN; if (Number.isNaN(myNumber)) { console.log("myNumber is NaN"); } else { console.log("myNumber is not NaN"); }

In other programming languages, such as Ruby, the NaN value can also be represented as an instance of either Float or BigDecimal. Therefore, the NaN data type is not unique to JavaScript.

To further understand the NaN concept in JavaScript, we can refer to the ECMA-262, 13th edition, which is the ECMAScript Language Specification. According to section 4.4.24, the Number type is defined as a "set of all possible Number values, including the special 'Not-a-Number' (NaN) value, positive infinity, and negative infinity." Subsequently, section 4.4.27 clarifies that NaN is a "number value that is an IEEE 754-2019 'Not-a-Number' value."

What is the IEEE 754 standard?

First published in 1985, the primary goal of the IEEE 754 standard is to provide a consistent computational method using floating-point numbers across various processing environments, be it software, hardware, or a mix of both. Along with specifying formats and methods for floating-point arithmetic in computer programming environments, IEEE 754 also defines a set of special values. These special values include:

  • 0: Both -0 and +0 are distinct values, although they are equal. Here's an in-depth article explaining both zero values in JavaScript.
  • Denormalized number: A number that lies between the smallest normalized positive floating-point number and zero.
  • Positive and negative Infinity: Values representing infinitely large positive or negative numbers.
  • NaN: A numeric data type that cannot be represented within the computing system.

IEEE 754 defines two types of NaN - a quiet NaN (qNaN) and signaling NaN (sNaN). The primary difference between the two is that sNaN will cause an exception when used in arithmetic operations, whereas qNaN will not. In JavaScript, it appears that all NaN values are quiet.

Additionally, the standard defines an interesting list of special operations and their results:

shell
number ÷ Infinity = 0 number ÷ -Infinity = -0 ±Infinity × ±Infinity = ±Infinity ±non zero number ÷ ±0 = ±Infinity number × ±Infinity = ±Infinity Infinity + Infinity = ±Infinity Infinity -Infinity = +Infinity -Infinity Infinity = Infinity -Infinity + Infinity = Infinity ±0 ÷ ±0 = NaN ±Infinity ÷ ±Infinity = NaN ±Infinity × 0 = NaN NaN == NaN (also === in JS) // False
shell
number ÷ Infinity = 0 number ÷ -Infinity = -0 ±Infinity × ±Infinity = ±Infinity ±non zero number ÷ ±0 = ±Infinity number × ±Infinity = ±Infinity Infinity + Infinity = ±Infinity Infinity -Infinity = +Infinity -Infinity Infinity = Infinity -Infinity + Infinity = Infinity ±0 ÷ ±0 = NaN ±Infinity ÷ ±Infinity = NaN ±Infinity × 0 = NaN NaN == NaN (also === in JS) // False

Most of the bits in the NaN encoding are not used to store meaningful information, which means they can be manipulated to store actual data - this is known as NaN-tagging or NaN-boxing. By using NaN-tagging, programmers can add additional type information to values without having to create new data types or use external metadata.

Testing for NaN values: Number.isNaN() vs isNaN()

JavaScript doesn't have a single NaN value, in fact, according to ECMA-262, there are "18,437,736,874,454,810,627 (that is, 264 - 253 + 3) values, representing the double-precision 64-bit format IEEE 754-2019 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9,007,199,254,740,990 (that is, 253 - 2) distinct 'Not-a-Number' values of the IEEE Standard are represented in ECMAScript as a single special NaN value. [...] In some implementations, external code might be able to detect a difference between various Not-a-Number values, but such behaviour is implementation-defined; to ECMAScript code, all NaN values are indistinguishable from each other."

Since NaN does not equal itself (in some programming languages, self-comparison is a widely used approach to test for NaNs), we need to use special methods such as Number.isNaN() or isNaN() to detect them.

It should be noted, that same-value equality methods, e.g. Object.is or same-value-zero equality methods, e.g. the ones used by Map and Set for comparing key equality, determine whether two values are functionally identical in all contexts. These methods will produce true when comparing NaN to itself.

If you're interested in learning more about how to use Set, in particular to remove duplicated items from an array, you might find this article helpful: Removing Duplicates with Map In JavaScript.

While both methods serve this purpose, they differ in their approach. Number.isNaN() is more precise, as it only returns true if the value is currently NaN and of the number type. On the other hand, isNaN() returns true if the value is currently NaN or if it will become NaN after being coerced to a numeric value. This distinction can lead to some unexpected results when using isNaN(), as it may return true for non-number values that are coerced to NaN. In TypeScript, calling isNaN() with an argument that is not of type number will result in a type error, alerting the programmer to the potential issue. As a result, when testing for NaN values, it is generally recommended to use Number.isNaN() for its higher accuracy and stricter type checking.

To better illustrate the differences between these testing methods, here's a table comparing the output of Number.isNaN(), isNaN(), and typeof value === "number" for various types of values.

Input valueNumber.isNaN(value)isNaN(value)typeof value === "number"
NaNtruetruetrue
undefinedfalsetruefalse
nullfalsefalsefalse
truefalsefalsefalse
"123"falsefalsefalse
123falsefalsetrue
[]falsefalsefalse
{}falsetruefalse

MDN provides a helpful explanation about testing against NaN.

Conclusion

NaN, or "Not a Number," is a special value found in JavaScript and many other programming languages. It represents undefined or unrepresentable values resulting from arithmetic operations. Although it may seem counterintuitive for NaN to be considered a number, it is actually a result of the widely adopted IEEE 754 standard for floating-point arithmetic.

Understanding the behavior and type of NaN in JavaScript is important for developers working with numeric operations. To detect NaN values, developers can use methods such as Number.isNaN() and isNaN(). These methods help ensure the accuracy and reliability of numeric operations in JavaScript.

References and resources