Mauro Bringolf

Currently geeking out as WordPress developer at WebKinder and student of computer science at ETH.

Bug report: Compute the 5 next months with modulo arithmetic

December 6, 2017

This post is about a stupid bug I wrote and what I learned from it. The goal of this format is to reduce the chance repeating a mistake and possibly prevent someone else from making it entirely. I will try to write more of these posts because I inevitably will write more incorrect code. Some errors are simple and easy to explain while others are more subtle and require more context. The one I write about today belongs to the former type. In fact it is a pretty embarrassing one but that’s the whole point right?

I encourage anyone to write this kind of post. If you do, please let me know so I can learn from your mistakes!

The problem

Given a month represented by a number between 1 and 12, compute the number representations of the 5 months following it. Here are a few example inputs and outputs:

$$ 5 \mapsto \{ 6,7,8,9,10 \} $$

$$ 8 \mapsto \{ 9,10,11,12,1 \} $$

$$ 12 \mapsto \{ 1,2,3,4,5 \} $$

It is a purely mathematical function that needs to be implemented. And a simple one indeed, but hey: I managed to get it wrong.

The bug

Now comes the embarrassing part, here is my fairly old PHP code trying to implement this:

// Input month given in $month
$next_months = [];
for($i = 1; i < 6; ++i) {
  $next_months[] = $month + $i % 13;

This is completely wrong. Probably the worst mistake is that I got operator precedence wrong: Modulus (%) binds stronger than addition (+). Therefore $i % 13 is evaluated first and will simply result in $i because it is always smaller than 13. But even if you correct the order of operations with parentheses, the result will still be wrong. I honestly cannot remember how I came up with this, but I probably thought things will get refactored and cleaned up anyways. It often doesn't until things start to break.

The fix

Since the error was purely mathematical, the fix is too. There are many other possible correct solutions involving PHP built in functions handling dates. However, I decided to keep this purely arithmetic. I simply wanted to know the mathematical function that I am trying to implement. It is enough to take a step back and realize that the function for the next month behaves almost like the modulo 12 function. It maps an integer onto its remainder when divided by twelve:

\(x\) 0 1 2 \( \dots \) 11 12 13 14 \( \dots \)
\( x \mod 12 \) 0 1 2 \( \dots \) 11 0 1 2 \( \dots \)

I say almost, because it is shifted by one. We do not have a month for the integer 0, but we do have one for 12. But that is just a matter of identifying things! So I can map the numbers representing months onto these by subtracting one. First I subtract one to get the corresponding number in the system above. Then I perform the modulo 12 operation and transform the result back into the original space by adding one:

nextMonth(x) := ((x - 1) \mod 12 ) + 1

The corresponding PHP code then becomes:

// Input month given in $month
$next_months = [];
for($i = 1; i < 6; ++i) {
  $next_months[] = (($month - 1 + $i) % 12) + 1;

Again, I am not saying this is the best way to do this. At least for me it obviously is not, because I got it wrong the first time. The purpose of this post is to help me get modulo right the next time I need it.

And to realize that all code is error prone, even "trivial" things like this.