How to debug NaN and NaN propagation bugs in JavaScript

Debugging NaN (not a number) related bugs in JavaScript can be frustrating. NaNs propagate, which means they can spread themselves around your data structures very quickly under some circumstances, and it can be time-consuming and somewhat tedious to track down the root cause. If you’re just building CRUD apps you may never encounter any NaN related issues, but the moment you start working on apps that involves a reasonable amount of numerical manipulation they can start to appear much more regularly. In this post I’ll share what I’ve learned through debugging numerous NaN related issues I’ve encountered whilst building games for arcade.ly.

In this post I’m going to outline some basics (e.g., what is NaN?), then work through common causes and manifestations of NaN related bugs, describe NaN propagation with an example, show how you can verify that you do have a NaN bug, and then look at three different strategies for debugging that you may find helpful.

So let’s start with the basics: what is NaN?

NaN is a special global property value in JavaScript whose value means “not a number”.

Any time you use a variable whose value is NaN in an expression, the result of that expression with be NaN, or false if it was any kind of comparison. You also can’t really do meaningful comparisons with NaN: for example, the expression NaN === NaN will always return false.

Fortunately you can check for NaN by calling isNaN(expression), which will return true if and only if the value of expression is NaN. This will come in handy later on when we look at debugging strategies.

In a typical CRUD app you may never encounter NaN, but as soon as you get into any form of numerical manipulation it’s pretty likely you’re going to run into it sooner or later.

Examples of such apps are:

  • Games

    • Particularly anything real-time: arcade games, shooters, platformers - anything that involves real-time movement or collision detection
    • Turn-based games can also experience NaN issues where numeric data is involved (RPGs, RTS games, etc.), but their slower pace tends to mean that these bugs are often easier to reproduce and debug: you can often isolate an exact sequence of steps required to reproduce a bug, especially if you can replicate a particular state in the game
    • I’ve run into NaN issues in both my games, Star Citadel (a version of Star Castle), and Shoot The Rocks (a version of Asteroids) which make extensive use of arithmetic, trigonometry, along with basic mechanics and some matrices
  • Data visualisation

    • Such apps will often take a feed of data from the back end, either on demand or in real-time: if that data is manipulated in any way beyond just throwing it at a charting library the possibility of NaN bugs creeping in exists (in theory it still exists with the library, but the probability of problems arising is much lower), and especially where custom visualisations are used

In some sense there are innumerable sources of NaN bugs but in my experience they tend to fall into a few common classes:

  • Divide by zero
  • Looking something up in an array using an index value that doesn’t exist, like a non-integral for looking up corresponding values that are approximated for their integral indices, or using a negative index rather than forcing it to positive (e.g., by adding 360 degrees)
  • Using an object in arithmetic rather than one of its properties (check the commit log for examples!)

1. Divide by zero

You attempted to divide a number by zero, and since the result of division by zero is undefined, the result of your division is NaN.

In some languages and platforms - notably those that are “strongly typed” - you’d see something like a DivideByZeroExeption thrown here. For example, the .NET runtime will throw a DivideByZeroException but only for integral or decimal types. It will not throw it for arithmetic involving 32-bit (float) or 64-bit (double) floating point numbers, since these have special values representing NaN as defined by the IEEE.

JavaScript is “weakly typed”, which means that any variable can be of any type, and can even change its type. For example, defining a variable as var myVariable = 1; and then following up with an assignment like myVariable = 'wibblefarb'; is entirely acceptable.

In terms of numbers therefore, JavaScript doesn’t have any distinction between integer and floating point values: strictly speaking all numbers are represented as 64-bit floating point values. These of course have that special representation for NaN. Hence, as aggravating as it may be, there is no exception or error if you do something like dividing by zero that results in a NaN.

(Worth pointing out that there’s nothing stopping JavaScript runtimes, such as Google’s V8 or Mozilla’s SpiderMonkey, from representing numbers as integers under the hood as an optimization, but exposing them as 64-bit floats when needed, but this isn’t something visible to you as a programmer.)

Nobody intentionally divides by zero, but there are plenty of ways it can happen inadvertently, so let’s look at a simple trigonometry example based on a common scenario in many games.

In both of my online arcade games there are enemies that need to track the player’s spaceship and rotate to face the player, either so they can chase the player down, or fire directly at them.

In both cases we know where the enemy is, and we know where the player is, and we need to use these facts to determine the angle of rotation required for the enemy to point directly at the player.

Here, in Shoot The Rocks (Asteroids), the flying saucer needs to know the angle it should fire at to hit the player’s space ship.

Flying saucer targetting the player's spaceship in Asteroids/Star Castle.

We need to know the angle A, relative to straight up, so that the flying saucer can fire at the player. In reality we’re going to add some random error to this, otherwise the game would be too hard, but we start with knowing where we should shoot. Note that Shoot The Rocks isn’t very sophisticated, so there’s no sense of the flying saucers shooting at where the player is most likely to be when the bullet reaches them: they just target where the player is now.

In the next shot, again from Shoot The Rocks/Asteroids, the satellites home in the player, so the satellite needs to know which direction to head in to eventually collide with the player’s spaceship.

TODO: SCREENSHOT HERE

Again, the angle A shows the direction, relative to straight up, that our satellite needs to be headed in.

TODO: SCREENSHOT HERE

Finally, in Star Citadel (Star Castle) the central cannon needs to track the player’s spaceship so that it can fire at the player whenever it gets a clear line of sight.

TODO: SCREENSHOT HERE

These behaviours are all slightly different, and I might talk about them in more detail in future, but the key point is finding that angle A, and this is where the trigonometry comes in.

To work out A we actually need to know both the sine of A, and the cosine of A. This is because the values of both sine and cosine are periodic oscillations: they repeat every 360 degrees and, for most values of both, there are two possible matching angles.

As you can see from the following diagram, sine is 0.5 at both 30° and 150°:

Sine and cosine functions plotted for angles from 0° to 360° inclusive.

As the chart also shows, sine and cosine are out of phase by 90 degrees, so if we have the values of both functions we can unambiguously resolve the corresponding angle. In fact we really only need to know if the cosine is positive, negative or zero to be able to pick the correct angle that matches the sine.

You might remember, from a mnemonic or rhymn at school (SOHCAHTOA, or whatever), that sine and cosine are defined for angle A as follows:

\[sin A = \frac{Opposite}{Hypotenuse}\]

\[cos A = \frac{Adjacent}{Hypotenuse}\]

Both of these equations can lead to instances of division by zero where the length of \(Hypotenuse\) is 0.

This will occur when the coordinates of, say, the flying saucer and the player’s ship, or the cannon and the player’s ship, are the same, because the length of the \(Hypotenuse\) is defined by Pythagoras’ theorem:

\[Hypotenuse^2 = Opposite^2 + Adjacent^2\]

which rearranges to:

\[Hypotenuse = \sqrt{Opposite^2 + Adjacent^2}\]

Hopefully your collision detection will have kicked in but you can’t guarantee that this will be the case if both objects are moving fast and, of course, depending on the order in which you choose to detect collisions and select firing solutions.

This section is still under construction.

Talk about classes that TypeScript can help avoid

This section is still under construction.

This section is still under construction.

What is NaN propagation and how does it occur?

This section is still under construction.

How do you tell objectively when you have a NaN bug?

This section is still under construction.

This section is still under construction.

//- For IE11, so that the GDPR script won't break //- Information on use of nomodule came from: //- https://gist.github.com/mgol/560a942d25ee8ab18349