Skip to content

Practice problem

Class Average To Two Decimals

Easy

Solve in Python · graded against 2 sample tests and a hidden test set in your browser · Published

The Bug Report

The program reads a set of test scores and prints the class average to exactly two decimal places. It runs, it never raises an error, and the output is always formatted correctly — two digits after the point, every time. The digits themselves are wrong.

Input that shows the problem

Input
4
10 20 30 45

What the program prints

Output
26.00

What it should print

Output
26.25

The four scores add up to 105, and 105 divided by 4 is 26.25. The program agrees on the whole-number part and then reports the fraction as nothing at all. Across other inputs the same thing shows up: the digits after the decimal point are always 00, no matter what the scores are.

Input Format

The input has exactly two lines:

  • Line 1: a single integer n, the number of scores.
  • Line 2: n integers separated by single spaces, the scores themselves.

Output Format

Print one line containing the average, rounded to exactly two decimal places. Always print both decimal digits, so an average of 5 prints as 5.00 and an average of 7.5 prints as 7.50.

How to Debug It

The most useful thing in the report is that the two decimals are always 00. That is not what a rounding mistake looks like — a rounding mistake gets the last digit wrong occasionally. Digits that are always zero means the fractional part is not being rounded badly, it is not arriving at all. Reproduce it with the smallest input that must have a fraction: two scores whose total is odd, such as 7 8, where the answer has to be 7.50.

Now decide which half of the program to suspect, because there are only two candidates: the function that computes the average, and the format string that prints it. Test them separately rather than reading both. Print the return value of average(scores) on its own line, with no formatting at all, right before the formatted print. If the unformatted value already shows no fraction, the formatting is innocent and the arithmetic is where the fraction is being lost. Confirm it the other way round too, by formatting a value you make up: print(f"{7.5:.2f}") proves in one line that .2f can show a fraction when it is given one.

That narrows the search to a single expression — the one that divides the total by the count — and gives you a hypothesis about what that expression returns. Check the type of its result, not just its value, because a whole number and a whole-number-valued fraction print identically once .2f gets hold of them. Change one operator, re-run 7 8, re-run the failing sample, then re-run the second sample, whose average genuinely is a whole number, to be sure it still prints 5.00 and not 5.

What Was Wrong

The function ended with return total // len(scores).

In Python, // is floor division, not a cosmetic variant of /. Given two integers it performs the division and then discards everything after the decimal point, rounding down towards negative infinity, and it returns an int. So 105 // 4 is 26, not 26.25. The fraction was gone before the value ever reached the print statement.

What made this so easy to miss is the .2f format specifier. Handed the integer 26, it faithfully prints 26.00, because formatting a whole number to two decimal places is a perfectly reasonable thing to do. The output therefore looks like a properly computed average with a fractional part that just happens to be zero — every single time. The formatting did not hide a crash; it manufactured a plausible-looking answer out of a value that had already lost its information.

The fix is one character, using true division:

Python
return total / len(scores)

/ between two integers in Python 3 always produces a float, keeping the fractional part, so 105 / 4 is 26.25 and .2f has something real to round. The second sample still prints 5.00, because 15 / 3 is the float 5.0 and the format specifier supplies the trailing zeros — which is where those zeros were always supposed to come from.

Common Mistakes

  • Keeping // and trying to recover the fraction afterwards, for example by computing the remainder separately and pasting the digits on. It can be made to work, but it is a hand-rolled reimplementation of division that has to get rounding right on its own, and it will disagree with / on some input. Use the operator that was meant.
  • Reaching for round(total / len(scores), 2) and printing that. round returns a number, not text, so an average of 5.0 prints as 5.0 and an average of 7.5 prints as 7.5 — one decimal digit, not the two the output format requires. Rounding and formatting are different jobs; .2f does both.
  • Assuming // and / only differ on negative numbers. They differ on every division with a remainder. The direction of rounding is what changes for negatives; the loss of the fraction happens for positives just as completely.

Sample tests

Sample 1

Input

4
10 20 30 45

Expected output

26.25

The four scores total 105, and 105 divided by 4 is exactly 26.25.

Sample 2

Input

3
5 5 5

Expected output

5.00

The average is a whole number here, so the broken program prints the right thing by accident — the two trailing zeros come from the formatting, not from the arithmetic.

Your solution

Run tries the first sample. Submit grades against every sample and the hidden tests. Your code is saved in this browser as you go.

Ctrl/Cmd+Enter to run

Press Esc then Tab to move keyboard focus out of the code editor.

Ready
Output will appear here after you run your code.

More problems

All practice problems →