Card 06/ 09
Off-by-One Errors in Loop Conditions
Why a limit of three attempts can send a payment four times.
An off-by-one error is a loop that runs one time too many or one time too few. In Java the usual cause is a counter that starts at 0 compared with <=, or a counter that starts at 1 compared with <.
A payment service is configured to try a payment at most three times. A support ticket shows four attempts in the log, four charges on the customer's card, and a customer who is unhappy about the fourth.
int maxAttempts = 3;
int attempt = 0;
while (attempt <= maxAttempts) {
send(payment);
attempt++;
}attempt starts at 0, and the condition lets the body run while it is 0, 1, 2 and 3. That is four values, so the body runs four times for a limit of three. With a counter that starts at 0, <= always runs one more pass than the number on the right.
| Condition | Highest value | Passes | Outcome |
|---|---|---|---|
attempt < maxAttempts | 2 | 3 | Three attempts, as configured |
attempt <= maxAttempts | 3 | 4 | One attempt too many |
attempt < maxAttempts - 1 | 1 | 2 | One attempt too few, and nothing says so |
Pick one convention and keep it everywhere: start the counter at 0 and compare with <, or start it at 1 and compare with <=. Mixing the two is where almost every off-by-one loop comes from. Then check the boundary once by hand: write down the first and the last value the counter takes, and count them.
← → move through sections and cards