Fun with JavaScript Numbers

Colin Ihrig
Share

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.