Beware of Division by Zero in Java

Beware of Division by Zero in Java Featured Image

As with most posts in this series, a weird problem cropped up in one of my courses, so I wanted to talk about it. The issue today is all about what happens when you divide by zero in Java.

Table of Contents

Background

For a little context, I figured I’d share why I’m writing this article. Every semester, I teach a course on software components. As an educator, I see it as my job to make sure students have proper support and guidance. Much of that support comes in the form of anticipating issues that students might encounter.

As I’ve taught the course a few times, I’ve noticed patterns in the way students grapple with the material. For instance, very early in the course, we ask the students to compute a square root using Newton iteration. The process looks something like this:

  1. Take a guess, `g`, at the square root of a number, `x` (e.g., `x` itself is a great starting point)
  2. Square `g` and subtract `x` from it. Then, divide the result by `x`. That gives us some error, `e`
  3. If `e` is close enough to 0, then we know we have the right `g`. We’re done!
  4. If `e` is not close enough to 0, then we need to take another guess.
  5. To compute a new `g`, we can take `g` and add it to the ratio of `x` over `g`. This sum can then be halved to give us our new `g`.
  6. Repeat steps 2-5 as needed.

To see how this works in practice, let’s try to predict the square root of 9. To start, we take a guess of 9. Our error comes out to 8 (i.e., (9 * 9 – 9) / 9). This is not close enough to 0. Our updated guess is 5 (i.e., (9 + 9 / 9) / 2). The error for 5 comes out to 1.78. Much better, but we can do better. Our updated guess is 3.4 which gives us an error of .28. Again, we’re getting closer. After that, our guess becomes 3.02, at which point we might stop (if we deem this close enough).

Now, the reason I show you this is because this process involves a potential division by 0 when `x` is 0. As a result, we usually ask students to handle this. Unfortunately, what ends up happening is that students will notice that their code works even when this division by 0 occurs. How is this possible? That’s the topic of todays article!

The Division by Zero Error in Java

If you’ve ever messed around with algebra, you probably know that division by zero is a big no-noOpens in a new tab.. I don’t have the math skill to explain why, but it makes somewhat intuitive sense, right? What does it mean to divide something into zero parts?

Because division by zero causes so many problems, programming languages have their own ways of dealing with it. For example, in Java, integer division by zero will cause an ArithmeticException. Here’s an example using JDoodle:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at MyClass.main(MyClass.java:6)

Personally, I’m a huge fan of errors like these because they give me some place to look when things go wrong. That said, I understand why developers sometimes avoid them due to the complexity they introduce.

Introducing NaN

Unfortunately, Java does not always provide this nice ArithmeticException in all cases—specifically when working with doubles. In the example I mentioned in the background, we compute the square root using doubles. As you saw, this more or less goes well, but there is one scenario where it does not: when x = 0.

To illustrate this, let’s try going through the same list of steps above. For example, we’ll start computing the square root of 0 by taking a guess, `g`, of 0. To be clear, both `x` and `g` are doubles. As a result, when it comes to computing the error, we get the following expression: `(0 * 0 – 0) / 0`. When simplified, we end up with the following expression: `0 / 0`. If these were integers, our program would crash as expected. Instead, our expression evaluates to `NaN`.

`NaN` is a bit of a weird value. It literally means “not a number,” but it can be stored in a double variable. As a result, it’s somewhat mischievous. To make matters worse, it won’t cause obvious problems when it is computed. For example, `NaN` can be used in relational expressions just like any double, so don’t expect it to cause any errors as it propagates.

In our case, when `NaN` is generated, it is then immediately checked if it’s close enough to `x` by using some threshold (e.g., `NaN >= .0001`). Because `NaN` is not a number, this expression always returns false. Up until this point, false would mean our condition was met, so we could return our guess as the square root. Funnily enough, because we set our first guess to `x`, we’ll return `x`. And since `x` happens to be its own square root, we might argue that the code works.

But the question is: does the code work? This is a bit of a philosophical question. After all, when I teach, I usually define correctness as a function whose set of outputs exist in the set of expected outputs. Using this black box definition of correctness, we might not care that our square root function accidentally came upon the right answer. And for our code golf friends, we might even prefer this “bug” to computing square roots. That said, there is something uneasy about the way things work out.

But It Works!

Every day, folks are going through code review processes while receiving comments like “this is somewhat of a hack” and “this has a bad smell,” and I’m starting to wonder if comments like this are valid. After all, the square root code works! As a result, I started to question some of the many assumptions we make about coding. For example, what makes code hacky? What makes code have a bad smell? Here are some discussions I managed to drum up:

Perhaps in a future article I might go down this philosophical rabbit hole. For now though, I need to call it a day! As always, here are some other coding tangents you might enjoy:

With that said, thanks for sticking around. See you next time!

Coding Tangents (43 Articles)—Series Navigation

As a lifelong learner and aspiring teacher, I find that not all subjects carry the same weight. As a result, some topics can fall through the cracks due to time constraints or other commitments. Personally, I find these lost artifacts to be quite fun to discuss. That’s why I’ve decided to launch a whole series to do just that. Welcome to Coding Tangents, a collection of articles that tackle the edge case topics of software development.

In this series, I’ll be tackling topics that I feel many of my own students have been curious about but never really got the chance to explore. In many cases, these are subjects that I think deserve more exposure in the classroom. For instance, did you ever receive a formal explanation of access modifiers? How about package management? Version control?

In some cases, students are forced to learn these subjects on their own. Naturally, this forms a breeding ground for misconceptions which are made popular in online forums like Stack Overflow and Reddit. With this series, I’m hoping to get back to the basics where these subjects can be tackled in their entirety.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts