Card 06/ 09

GotchaDifficulty: Intermediate1 min

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.

java
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.

What each condition does to a counter starting at 0, with a limit of three
ConditionHighest valuePassesOutcome
attempt < maxAttempts23Three attempts, as configured
attempt <= maxAttempts34One attempt too many
attempt < maxAttempts - 112One 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.