Concepts / Using Print Statements for Debugging

Using Print Statements for Debugging

Debugging by bisection cuts debugging time from linear to logarithmic by eliminating half the search space with each checkpoint check.

  • Programming

The Cost of Checking Every Line

When a program produces a wrong result, a natural response is to inspect each line in order until something looks wrong. This is a linear search through the program: the search may move from the first line to the second, then the third, and so on. In a 100-line program, this approach can require up to 100 checks in the worst case.

100-line programup to 100 checksLine-by-line searchone line at a time100-line programabout 7 checksBisection searchhalf at a timecheckschecks
How does the number of debugging checks change when checking every line compared with halving the search space?

Halving the Search Space

Bisection debugging divides the remaining suspect code into two regions. You check an intermediate value, then decide which region contains the problem. If the intermediate value is correct, the bug must be later. If it is incorrect, the bug must be earlier. You then repeat the same process inside the remaining region.

Worked example

Reducing a 100-line search

A 100-line program produces an incorrect result. Compare the remaining search area after successive bisection checks.

  1. 1
    First check

    Check an intermediate result that divides the program into two broad regions. The remaining search is about 50 lines.

  2. 2
    Second check

    Check the middle of that remaining region. The search is reduced to about 25 lines.

  3. 3
    Third check

    Another division leaves about 12 or 13 lines.

  4. 4
    Fourth check

    The suspect region is reduced to about 6 or 7 lines.

  5. 5
    Fifth check

    The remaining region is about 3 or 4 lines.

  6. 6
    Sixth check

    Only one or two lines remain as the likely location.

Result

A 100-line program can be narrowed to one or two likely lines in about six checks, or about seven checks when accounting for the final isolation.

100 linesinitial region50 linesafter check 125 linesafter check 212 or 13 linesafter check 36 or 7 linesafter check 43 or 4 linesafter check 51 or 2 linesafter check 6check 1check 2check 3check 4check 5check 6
What happens to the possible bug locations after each checkpoint check?

Finding the Correctness Boundary

The most useful question is not simply “Which line is wrong?” It is “Where does the program change from producing correct intermediate values to producing incorrect ones?” Bisection searches for this boundary. The last correct checkpoint tells you that the earlier region has produced an acceptable result. The first incorrect checkpoint tells you that the transition happened before or at that point.

Worked example

A boundary in a data-processing loop

A loop processes data and eventually produces a wrong result. You can inspect the result after selected stages of the loop.

  1. 1
    Check an early stage

    If the intermediate result is correct, the problem is probably in a later region of the processing flow.

  2. 2
    Check a later stage

    If the later intermediate result is incorrect, the bug lies between the last correct checkpoint and this first incorrect checkpoint.

  3. 3
    Repeat inside the region

    Add or move a checkpoint within that suspect region and continue narrowing it.

Result

The bug is isolated to the small region between the last correct intermediate value and the first incorrect one.

Checkpoints Inside Loops

An exact midpoint is often impractical. A loop may not have a convenient place to inspect a value at the exact middle line of a program. Instead, place checkpoints according to the loop's logical flow. Useful locations can include after the loop completes, at the end of an iteration, or after a key calculation. The objective is to divide the likely bug region into two reasonably balanced parts based on how the program works, not merely on line counts.

Input dataIteration endcheckpointKey calculationcheckpointLoop completecheckpointSuspect regionbetween correct andincorrect checksprocesscontinuecontinuenarrow
Where should checkpoints be placed in a loop when an exact midpoint is impractical, and how do they narrow the faulty region?
Best practice

For a loop that processes data and produces a result, first consider checkpoints after a meaningful processing stage. If that does not distinguish the correct and incorrect regions, inspect the end of an iteration or the result immediately after a key calculation. Select the location that makes the intermediate value easiest to verify.

Estimating the Number of Checks

To estimate the checks needed, imagine repeatedly cutting the remaining search region in half until only one or two possible lines remain. For 100 lines, the sequence is approximately 100, 50, 25, 12 or 13, 6 or 7, 3 or 4, and finally 1 or 2. This gives about 7 checks. For 1000 lines, linear search may require up to 1000 checks, while bisection requires about 10 checks.

Program sizeLinear searchBisection
100 linesUp to 100 checksAbout 7 checks
1000 linesUp to 1000 checksAbout 10 checks

The source examples show how repeated halving reduces the number of checks as program size grows.

100 lines0 checks50 lines1 check25 lines2 checks12 or 13 lines3 checks6 or 7 lines4 checks3 or 4 lines5 checks1 or 2 lines6 checkshalvehalvehalvehalvehalvehalve
How many checkpoint checks are needed to reduce a program of a given size to a single possible error location?

Common Mistakes

  • Common mistake

    Checking every line automatically

    What it looks like

    Starting at the first line and inspecting each line in sequence without using intermediate checkpoints.

    Why it fails

    A 100-line program can require up to 100 checks with this approach.

    Do this instead

    Begin with a checkpoint that divides the likely search region, then continue inside the region shown to contain the problem.

  • Common mistake

    Treating the exact middle line as mandatory

    What it looks like

    Trying to place a checkpoint at an exact line midpoint even though a loop has no meaningful value to inspect there.

    Why it fails

    The exact midpoint may be unclear or impossible to verify.

    Do this instead

    Choose a location based on logical code structure and ease of verification.

  • Common mistake

    Printing values that cannot be judged

    What it looks like

    Adding a checkpoint without knowing what the intermediate value should mean or whether it is correct.

    Why it fails

    The output does not help decide which half of the search region contains the bug.

    Do this instead

    Inspect an intermediate value with a clear expected meaning.

  • Common mistake

    Assuming every bug is one incorrect value

    What it looks like

    Using only one value check when the problem may involve operation order or interactions between multiple variables.

    Why it fails

    Some bugs are about order or interactions rather than a single incorrect value.

    Do this instead

    Use bisection as a narrowing strategy and combine it with step-by-step tracing, a debugger, or multiple checkpoints when necessary.

When Bisection Needs Help

Bisection is not always easy to apply. A complex data structure may be difficult to verify at one intermediate point. Some bugs depend on the order of operations or on interactions between several variables rather than on one incorrect value. In such cases, bisection still provides a useful way to partition the problem, but it may need to be combined with step-by-step execution tracing, a debugger, or multiple checkpoints.

Practice the Search

MEDIUM

A program has 1000 lines and produces an incorrect final result. Describe how you would use checkpoint checks to narrow the search. Then explain why choosing a meaningful checkpoint in a loop may be better than choosing the exact middle line by number.

Hints
  • Compare the worst-case number of checks for line-by-line search with bisection.
  • Imagine reducing the remaining region from 1000 to about 500, then about 250, and so on.
  • For a loop, consider the end of an iteration, after a key calculation, or after the loop completes.

What do you think happens?

A 100-line program is debugged by checking an intermediate result, then repeatedly checking the middle of the remaining suspect region. Which approach is expected to require fewer checks in the worst case?

  • Checking every line in order

  • Repeatedly halving the suspect region

  • Both approaches require about the same number of checks

Reveal answer
Answer:

Repeatedly halving the suspect region

The source example gives up to 100 checks for linear search and about 7 checks for bisection.

A Systematic Debugging Habit

  1. 1
    Identify an intermediate value or result that can be checked.
  2. 2
    Place a checkpoint that divides the likely bug region into two meaningful parts.
  3. 3
    Decide whether the checkpoint is correct or incorrect.
  4. 4
    Discard the region that cannot contain the bug.
  5. 5
    Repeat until the boundary between the last correct checkpoint and the first incorrect checkpoint is isolated.
  6. 6
    If the values are difficult to verify or the bug involves interactions or operation order, add tracing, a debugger, or multiple checkpoints.

Key Takeaways

  • Checking every line in order is a linear search and can require up to 100 checks for a 100-line program.
  • Bisection reduces the search region by about half after each useful checkpoint.
  • A 100-line program can be narrowed to about one or two lines in roughly six checks, or about seven checks overall.
  • Checkpoint locations should follow logical code structure and ease of verification, especially inside loops.
  • The target is the boundary between the last correct intermediate value and the first incorrect one.

Related concepts

More concepts starting with U