Fun with JavaScript Numbers
Data types are an essential component of every programming language, and numbers are perhaps the most important of all data types. After all, computers are really just expensive calculators. Like any worthwhile programming language, JavaScript supports numerical data. However, like many other aspects of JavaScript, numbers have several intricacies which can bite you if you’re not careful. This article explores numerical data and some of its quirks.
Note: before reading this article, you should have a basic familiarity with JavaScript data types.
The Number
Type
In JavaScript, all numbers are represented using the Number data type. This includes integers, real numbers, hexadecimal numbers, and numbers written in scientific notation. The following example verifies this by applying the typeof
operator to a variety of numbers. Each application of typeof
in this example returns number
.
typeof(100);
typeof(3.14);
typeof(0xDEADBEEF);
typeof(7.89e2);
In the previous example, the numbers took on a variety of formats. However, internally, all JavaScript numbers are actually represented as IEEE 754 floating point data. This is important because it means that JavaScript has no concept of integers, despite the language’s parseInt()
function. It also means that JavaScript math is not 100% accurate. For example, consider the following expression.
(0.1 + 0.2) === 0.3
If you are unfamiliar with floating point numbers, you would certainly expect this expression to evaluate to true
. After all, 0.1 + 0.2 does equal 0.3. However, due to the way floating point numbers work, the sum is actually 0.30000000000000004. The difference is slight, but it is enough to cause the entire expression to evaluate as false
.
Positive and Negative Zero
Another quirk of the IEEE 754 standard is the signed zero. This results in two zeroes – a positive zero, +0, and a negative zero, -0. This may seem strange, but the fun is just beginning. Clearly, these are two distinct values, otherwise there would only be a single zero. However, if you display either of the zeroes, the sign is dropped. For example, the following code attempts to display the two zero values, side-by-side.
alert((+0) + " " + (-0));
// displays 0 0
To make matters worse, JavaScript’s comparison operators can’t seem to tell the two values apart either, as shown in the following example.
alert((+0 === -0));
// displays true
alert((-0 < +0));
// displays false
There is a fairly simple workaround for this problem. In JavaScript, division by zero yields Infinity
. Similarly, division by negative zero yields -Infinity
. Therefore, to determine if a number is equal to -0, we must check that it is a zero, then perform division with it as the denominator, and check for -Infinity
as shown below.
function isNegativeZero(x) {
return (x === 0 && (1/x) === -Infinity);
}
Not-a-Number
JavaScript actually defines a number named Not-a-Number, or NaN
. NaN
is a falsy value that is used to represent non-numbers as numbers. This value is interesting because its very name precludes it from being a number, yet typeof(NaN)
is number
. NaN
is also fun because it is the only value in JavaScript that does not equal itself. For example, the following code will return false
.
alert((NaN === NaN));
// displays false
Instead of using the comparison operators, you can test for NaN
using the isNaN()
function, as shown below.
isNaN(1);
// returns false
isNaN(NaN);
// returns true
However, isNaN()
is also fun because it can be misleading. If you pass in a value that can be coerced to a number, isNaN()
will return false
. In the following example, isNaN()
is called with several values that are clearly not numbers. However, each call returns false
.
isNaN(true);
isNaN(false);
isNaN("");
isNaN(null);
// all return false
A better way to check for NaN
is by exploiting the fact that it is not equal to itself. The following function tests for NaN
using strict inequality. This function will only return true
for the value NaN
.
function isNotANumber(x) {
return x !== x;
}
Other Fun Times
There are a few other scenarios which can lead to problems with numbers. For starters, you should beware of old browsers that allow global properties like Infinity
, NaN
, and undefined
to be redefined to new values. For example, the following code could create a lot of problems if NaN
is used frequently. Luckily, modern browsers will ignore assignments to the previously mentioned properties. Strict mode goes one step further by turning these silent failures into errors.
NaN = 1;
...
isNaN(NaN);
// now returns false
Another fun to diagnose error comes from adding a number and a string. An example of this is shown below. In this case, string concatenation overrides addition. This causes foo
to be converted to the string "100"
. The final result is the string "1001"
, which is much different from the expected value of 101. This type of error is more common than you think, and tends to occur when reading user input.
var foo = 100;
var bar = "1";
alert(foo + bar);
// displays "1001"
Conclusion
This article has explored some of the idiosyncrasies of numbers in JavaScript. Hopefully, you now understand why these issues arise, and how you can avoid them. And, although you may not run into cases like the negative zero very often, at least now you are prepared.