Debugging logic errors

Finding the mistake that gives no message: watching, printing, stepping and common bugs.

F6.4Robust programsGCSE15 min

Do this lesson in the simulator

Syntax errors and runtime errors announce themselves. A logic error does not: the program runs, finishes, and does the wrong thing. Finding one is detective work, and like detective work it goes faster with a method. This lesson is that method, and the tools that help.

Step 1: know what should happen

You cannot find a mistake until you know exactly what right looks like. This program should drive a square of 20 cm sides and end where it started, facing the way it began:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

side = 20
for i in range(3):
    forward(60, distance=side)
    turn_left(30, angle=90)
print("back at", position(), "facing", round(heading()))

Run this in the simulator

Run it. It ends in the wrong place, facing the wrong way. No error message. The trail on the mat is the first clue: count its sides.

Step 2: find where it first goes wrong

Look for the first moment the program does something different from what it should. Everything before that point is fine, and the bug is at or just before it. Three ways to find that moment:

  • Watch it. The trail shows three sides, not four, and each turn goes the wrong way.
  • Print it. Add a print inside the loop showing i, position() and heading() each time round: the program writes its own trace table.
  • Step through it. Press Debug. The program stops before each line; press Step to run one line at a time, and read the variables beside the editor as they change.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

side = 20
for i in range(3):
    print("side", i, "starts at", position(), "facing", round(heading()))
    forward(60, distance=side)
    turn_left(30, angle=90)
print("back at", position(), "facing", round(heading()))

Run this in the simulator

The printout shows i going 0, 1, 2: only three times round. And the heading goes to 270, then 180: anticlockwise, when the square was meant to turn clockwise like a positive angle.

Step 3: form a theory and test it

Say what you think is wrong, change one thing, and run again:

  1. Theory: range(3) gives three sides. Change it to range(4). Run: four sides now, but still turning the wrong way.
  2. Theory: turn_left(30, angle=90) turns anticlockwise. Change it to turn_right(30, angle=90). Run: a square, back at the start.

Changing one thing at a time matters. Change both at once, and if the result is still wrong, you do not know which change helped and which did not.

Common logic errors

Error Example How it shows
Off by one range(3) for four things one too few (or too many) times round
Wrong operator < instead of <= the edge case, exactly at the limit, is wrong
Wrong sign turn_left(30, angle=90) for turn_right(30, angle=90) the robot goes the opposite way
Reset in the loop total = 0 inside the loop a total that never grows
Wrong variable print(side) when you meant total the right kind of value, but the wrong one
Order of steps reading the sensor before driving, when it should be after values that lag one step behind

Most bugs are one of these. When a program is wrong, run down the list.

Explaining it out loud

Programmers sometimes explain their code, line by line, to a rubber duck on the desk. It sounds silly, and it works: saying what each line should do makes you notice the line that does not. A classmate works even better than a duck.

Task: fix the square

This program should drive a square of 25 cm sides and finish back at the start, facing the way it began. It has three logic errors. Find them with the trail, prints or Debug, and fix them one at a time.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

side = 25
for i in range(3):
    side = 20
    forward(60, distance=side)
    turn_left(30, angle=90)

Challenges

  1. Write a program with an off-by-one error in it for a partner, and time how long they take to find it with Debug.
  2. The parking sensor from F2.8 stops too close to the wall at high speed. Is that a logic error? How would you find the best stopping distance?
  3. Add a print that shows a variable's value and its type. When would the type be the clue?