Exercises — while loop — condition-based, infinite loop dangers
Everything here builds on the parent topic. When a comparison or truth-value shows up, see Boolean expressions and comparison operators; when we count checks, that is an Off-by-one errors question; when we jump out early, that is break and continue statements.
Level 1 — Recognition
"Can you read a loop and say what it does?"
Recall Solution 1.1
The update is i += 2, so i climbs by 2 each round. We trace every check — including the final failing one, exactly like the parent's trace table. The number line below shows i hopping by 2 until it lands on 8 and the door i < 8 shuts.

| check | i |
i < 8? |
action |
|---|---|---|---|
| 1 | 2 | True | print 2, i→4 |
| 2 | 4 | True | print 4, i→6 |
| 3 | 6 | True | print 6, i→8 |
| 4 | 8 | False | exit |
Prints 2 4 6 — three numbers. The condition is checked 4 times (3 True + 1 final False).
Recall Solution 1.2
A while is a pre-test loop: it checks before the first run. First check is 0 > 5 which is False, so the body runs zero times. Nothing is printed. This is the "the body may run zero times" fact from the parent — the loop can be skipped entirely.
Recall Solution 1.3
- A — has an update
x -= 1that pushes towardx > 0becoming false. Terminates. - B — the body never changes
x.xstays 10,x > 0is forever True. Infinite loop. (The classic "missing update".) - C —
xgoes 10, 8, 6, 4, 2, 0. Atx == 0the check0 != 0isFalse. Terminates. (It happens to land exactly on 0 because 10 is even — see the L3 trap.)
Answer: B.
Level 2 — Application
"Can you write a straightforward loop from a spec?"
Recall Solution 2.1
i = 1 # Initialise
while i <= 5: # Condition
print(i)
i += 1 # Update
print("done")Prints 1 2 3 4 5 then done. All three loop ingredients are present — Initialise (i = 1), Condition (i <= 5), Update (i += 1) — the checklist defined at the top of this page.
Condition checks: i takes values 1,2,3,4,5 (all True) then 6 (False). That is 6 checks to print 5 numbers — the off-by-one final check.
Recall Solution 2.2
An accumulator pattern: one variable holds the running total, another is the counter.
total = 0
i = 1
while i <= 10:
total += i # add current i into the running total
i += 1 # update the counter
print(total) # 55The counter i rises 1→10; each round adds it into total. When i reaches 11 the check 11 <= 10 is False → exit. Prints 55.
Recall Solution 2.3
We don't know how many tries → while, not for. The update is the new input().
answer = "" # initialise so the condition can be checked once
while answer != "stop": # compare strings directly
answer = input("Type something (or 'stop'): ")
print("You stopped.")answer starts "" so the first check "" != "stop" is True → we enter and ask. Each input() overwrites answer; when the user types stop, "stop" != "stop" is False → exit.
Level 3 — Analysis
"Can you find why a loop misbehaves and predict its exact output?"
Recall Solution 3.1
Infinite loop. Adding 0.1 in binary floating-point never lands exactly on 2.0 — the running value overshoots to something like 2.0000000000000004, so x != 2.0 stays True forever. The printed values (rounded) look like they pass through 2.0, but the stored value never equals it.
Fix: never test floats with ==/!=. Use a boundary comparison:
x = 1.0
while x < 2.0:
x += 0.1This is the "float never equals" killer from the parent's danger table.
Recall Solution 3.2
Exact behaviour of the code as written: it prints 0 2 4 6 8 — but by a fragile route, and one tiny change makes it loop forever. Let us trace it honestly rather than guess.
i |
i < 10? |
i % 2 == 0? |
what happens |
|---|---|---|---|
| 0 | True | True | i += 1 → i = 1, continue (nothing printed yet) |
| 1 | True | False | print i - 1 = 0, i += 1 → i = 2 |
| 2 | True | True | i += 1 → i = 3, continue |
| 3 | True | False | print 2, i += 1 → i = 4 |
| … | … | … | pattern repeats: prints 4, 6, 8 |
| 10 | False | — | exit |
So the output is exactly 0 2 4 6 8. The point of this exercise: the continue path does carry an update (i += 1 sits before the continue), which is the only reason it terminates. This is the safe way to use continue.
Now the danger — move the update to the wrong side of continue:
i = 0
while i < 10:
if i % 2 == 0:
continue # <-- update was removed from this path
i += 1 # DEAD CODE: never runs, it's after continue
print(i - 1)
i += 1Trace: i = 0, condition True, 0 % 2 == 0 True → continue jumps straight back to the condition without changing i. i is stuck at 0 forever → infinite loop. See break and continue statements: continue skips everything below it in the body, including any update placed there.
Rule: when a path uses continue, put the update before the continue, never after. The first (working) version does this correctly; the second is the classic "update in the wrong place" infinite loop from the parent's danger table.
Cleaner fix (no continue at all):
i = 0
while i < 10:
if i % 2 == 0:
print(i)
i += 1 # single update on the common path — always runsRecall Solution 3.3
// is integer (floor) division — it halves and drops any fraction.
| check | n before |
n > 1? |
after n // 2 |
|---|---|---|---|
| 1 | 100 | True | 50 |
| 2 | 50 | True | 25 |
| 3 | 25 | True | 12 |
| 4 | 12 | True | 6 |
| 5 | 6 | True | 3 |
| 6 | 3 | True | 1 |
| 7 | 1 | False | exit |
Prints 50 25 12 6 3 1. Condition checked 7 times; loop ends with n = 1.
Level 4 — Synthesis
"Can you combine loops with conditions, break, and input into a small program?"
Recall Solution 4.1
This is the deliberate-infinite pattern from the parent: while True + a controlled break.
while True: # intentional infinite loop
cmd = input("> ")
if cmd == "quit":
print("Goodbye!")
break # the real, controlled exit
elif cmd == "hello":
print("Hi there!")
else:
print("unknown command")The break is the genuine stop sign. This is not an accidental infinite loop — the exit is explicit and reachable.
Recall Solution 4.2
Two loops: a validation loop (unknown tries) and a computation loop (accumulator).
n = 0
while n <= 0: # validation: re-ask on bad input
n = int(input("Positive integer: ")) # type conversion, see input() note
fact = 1
i = 1
while i <= n:
fact *= i # accumulate the product
i += 1
print(fact)For n = 5: fact goes 1·1=1, ·2=2, ·3=6, ·4=24, ·5=120. Prints 120.
The validation loop uses int(input(...)) for input() and type conversion; the computation loop is a multiply-accumulator, sibling of Ex 2.2's sum-accumulator.
Level 5 — Mastery
"Can you turn a raw goal into a correct, terminating loop and justify termination?"
Recall Solution 5.1
k = 1
while 2 ** k <= 1000: # keep going while NOT yet past 1000
k += 1
print(k) # 10Powers of two: 2,4,8,16,32,64,128,256,512,1024. At k = 10, 2**10 = 1024 > 1000, so the condition 1024 <= 1000 is False → exit with k = 10.
Termination proof: 2**k strictly increases as k increases, and it is unbounded, so it must eventually exceed 1000. Since k rises by exactly 1 each round, the condition is guaranteed to flip to False — this is the parent's convergence guarantee (a monotonic quantity crossing a fixed boundary).
Recall Solution 5.2
n = 6
steps = 0
while n != 1: # stop when we hit 1
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
steps += 1 # update lives on EVERY path (no continue trap)
print(steps) # 8Trace from 6: 6→3→10→5→16→8→4→2→1. That is 8 steps. Note the update steps += 1 sits after the if/else on the common path — avoiding the L3 continue pitfall. (Collatz termination for all n is famously unproven in general, but for n = 6 it clearly reaches 1.)
Recall Solution 5.3
Peel off the last digit with % 10, drop it with // 10, and build the reversed number by shifting.
n = 1234
rev = 0
while n > 0:
last = n % 10 # last digit
rev = rev * 10 + last # append it to rev
n = n // 10 # remove last digit (this is the update)
print(rev) # 4321Trace: n=1234, rev=0 → rev=4, n=123 → rev=43, n=12 → rev=432, n=1 → rev=4321, n=0. Check 0 > 0 False → exit. Prints 4321.
Termination argument (this is the Mastery skill): name the quantity that shrinks. Here n is our decreasing measure. Each round n = n // 10 divides n by 10 and floors it, so for any n > 0, the new value n // 10 is strictly smaller than the old n (e.g. 1234 // 10 = 123 < 1234; even 1 // 10 = 0). A strictly decreasing sequence of non-negative integers cannot fall forever — it must reach 0 in finitely many steps (at most the number of digits of the start value). Once n = 0, the condition n > 0 is False and the loop exits. This is a variant argument: a non-negative integer measure that strictly decreases each iteration guarantees termination — the same reasoning pattern behind Ex 5.1's monotone quantity.
Recall Master check — cover the answers
- Ex 1.1 output? :::
2 4 6, condition checked 4 times. - Ex 2.2 total? :::
55. - Ex 3.2 exact output of the code as written? :::
0 2 4 6 8(it works because the update sits before thecontinue). - Ex 3.3 checks and final
n? ::: 7 checks, ends withn = 1. - Ex 4.2 factorial of 5? :::
120. - Ex 5.1 smallest
kwith2**k > 1000? :::10. - Ex 5.2 Collatz steps from 6? :::
8. - Ex 5.3 reverse of 1234? :::
4321.
Connections
- Parent topic — the concepts these exercises drill.
- for loop — iterating over sequences — when a count is known, prefer this.
- break and continue statements — Ex 3.2, 4.1, 5.2 hinge on these.
- Boolean expressions and comparison operators — every loop condition is built from these.
- input() and type conversion — Ex 2.3, 4.1, 4.2 drive loops with user input.
- Off-by-one errors — Ex 1.1, 2.1, 3.3 are all about the final failing check.