Off-By-One in a While-Loop Sentinel
A program reads daily temperatures from a scanner until it sees the sentinel value -999. The student writes:
int count = 0;
int sum = 0;
int t = in.nextInt();
while (t != -999) {
sum += t;
count++;
t = in.nextInt();
}
System.out.println(sum / count);
The input is 70 72 68 -999. The program prints 70, which is correct. But when the input is just -999, the program crashes. Separately, a reviewer notes that a legitimate temperature of -999 could never be recorded, and that the loop reads one extra value if the file ends without a sentinel.
1. Explain why the all-sentinel input `-999` crashes, and identify the two design weaknesses the reviewer flagged.
Read the worked answer
With input -999, the priming read sets t = -999, the loop body never runs, so count stays 0. The final line computes sum / count = 0 / 0, which throws ArithmeticException. The loop logic is correct; the post-loop code assumes at least one data value.
Reviewer's two weaknesses:
- Sentinel collision.
-999is a plausible (if extreme) temperature, so using it as a sentinel makes that value unrecordable. A better design uses a value outside the valid domain, or a count-prefixed input, orhasNextInt(). - Unchecked end-of-input.
in.nextInt()throwsNoSuchElementExceptionif the file ends without-999. The loop assumes a well-formed stream. A robust version checksin.hasNextInt()in the loop condition.
2. Rewrite the loop so it handles empty input gracefully and does not crash at end-of-file, keeping the sentinel check. State what the program should print for empty input and why.
Read the worked answer
int count = 0;
int sum = 0;
while (in.hasNextInt()) {
int t = in.nextInt();
if (t == -999) {
break;
}
sum += t;
count++;
}
if (count == 0) {
System.out.println("no data");
} else {
System.out.println(sum / count);
}
hasNextInt() guards both end-of-file and the priming read, so no NoSuchElementException. The break on the sentinel exits cleanly. For empty input, count == 0, so printing "no data" is correct - there is no meaningful average of zero values, and dividing would throw. The judgment call is that an average of an empty set is undefined, not zero; the program should say so rather than fabricate a value.