Debugging logic errors

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

F6.4Robust programsGCSE15 min

Covers OCR J277 2.3.2 Testing · AQA 8525 3.2.11 Robust and secure programming · Edexcel 1CP2 6.1.2 Read, write, analyse and refine programs · Edexcel 1CP2 6.1.5 Finding and correcting errors

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.

The debugging method: know what should happen, find where it first goes wrong, change one thing, run again, and repeat until it meets the brief1. what shouldhappen?know it exactly2. where does itfirst go wrong?watch, print, step3. a theory:change one thingonly onerun it again:does it meetthe brief?yesfixedno: back to step 2, with the new run
The method as a loop. Each time round, one change and one new run, until the program meets the brief, not just until it stops looking wrong.

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 clockwise, turning right at each corner, 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 about 270, then about 180: anticlockwise, when the brief says clockwise, which would take it to 90, then 180.

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, and it ends back at the start, but the square is drawn to the left of where it began, anticlockwise. Ending in the right place is not the same as meeting the brief.
  2. Theory: turn_left(30, angle=90) turns anticlockwise. Change it to turn_right(30, angle=90). Run: a square, back at the start.
The trail of the square program as written, after range(4), and after turn_right, each against the square the brief asks foras writtenrange(3), turn_left3 sides, turning leftends 20 cm left of the startfacing 90, not 0change 1: range(4)still turn_left4 sides, turning leftback at the startfacing 0, as it beganchange 2: turn_rightrange(4)4 sides, turning rightback at the startfacing 0, as it beganstart
The dashed square is what the brief asks for, clockwise to the right of the start. The first change fixes the number of sides but not the direction, so the square is on the wrong side even though the robot ends at home. Only after the second change does the trail match the brief. Headings are as heading() gives them: 0 at the start, counting clockwise.

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 clockwise, turning right at each corner, 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?

Past paper questions on this

Real exam questions that test this, each worked through with a program you can run.