When 89.99999999999999 Becomes an 89
Ms. Alvarez teaches an introductory CS course and sponsors the district science fair. The district's grade portal computes term averages with a Python script. Weights are typed as decimal literals: homework 0.20, quizzes 0.30, exams 0.50. Scores are stored as binary floating-point numbers. A student, Jordan, has homework scores 90, 90, 90, 90; quiz scores 89, 91, 90, 90; exam scores 89.5 and 90.5. Hand calculation gives exactly 90.0. The portal displays 89 because its final step uses int(average) instead of rounding. Jordan's parent emails: 'The math is wrong; my child should have an A.' The teacher confirms the raw stored value is 89.99999999999999. The registrar says grades are official once submitted and changes require a formal correction form. The principal asks Ms. Alvarez to respond within 24 hours and to recommend whether the script should be changed before the next term. The script is used by 12 teachers and has no tests.
1. Explain why the portal shows 89 when the hand calculation gives 90.0. Is Jordan's average mathematically 90? Use the given scores and weights.
Read the worked answer
Compute the category averages first. Homework: (90 + 90 + 90 + 90) / 4 = 90. Quizzes: (89 + 91 + 90 + 90) / 4 = 90. Exams: (89.5 + 90.5) / 2 = 90. The weighted average is 0.20(90) + 0.30(90) + 0.50(90) = 18 + 27 + 45 = 90.0 exactly. Mathematically, Jordan earned 90.0.
The stored value 89.99999999999999 is a floating-point representation artifact. Most decimal fractions, including 0.20, 0.30, and 0.50, cannot be represented exactly in IEEE 754 binary floating point. The script multiplies and adds those approximations, so a tiny error accumulates. The final int(average) truncates toward zero rather than rounding, turning 89.999... into 89. The display is wrong even though the underlying mathematics is not. The teacher should still use the official correction process because the portal record is official, but the correction is justified by the computation, not by sympathy.
2. Recommend a code fix and a process fix. Why might round(average) alone not fully solve the problem, and what should happen to Jordan's already-submitted grade?
Read the worked answer
The code fix should avoid binary floating point for exact decimal grading. Store scores and weights as scaled integers. For example, keep scores in tenths: 90.0 becomes 900, 89.5 becomes 895, and weights become 20, 30, and 50. Compute total_tenths = (hw_tenths * 20 + quiz_tenths * 30 + exam_tenths * 50) // 100, then apply an explicit rounding rule at the end. Alternatively, use Python's decimal.Decimal with a defined precision and rounding mode. Add unit tests for exact ties such as 89.5, values just below a boundary such as 89.49, and the current Jordan case.
round(average) alone is not enough because Python's round uses banker's rounding: round(2.5) is 2, and round(2.675, 2) can produce 2.67 because the input is already an imprecise float. The district must define whether .5 rounds up, down, or to even, and apply that rule consistently. For Jordan, submit the correction form with the hand calculation and the stored raw value, request a change to 90, and document that the change is a display/logic correction, not a special favor.
3. The principal wants a single display and rounding policy for all 12 teachers. Propose the policy and justify it with a concrete example of a borderline case.
Read the worked answer
Policy: category and term averages are computed from exact scaled integers, not binary floats. The unrounded term average is stored to at least one decimal place. The official term grade is the unrounded average rounded to the nearest whole number using half-up: a fractional part of exactly .5 rounds up; anything below .5 rounds down. The portal displays one decimal place so families can see the value used. No truncation is allowed at any step. Teachers may not manually override the computed value except through the correction form, and every override must include the calculation.
Example: a student has an unrounded term average of 89.5. Under half-up, the grade is 90. A second student has 89.49; the grade is 89. This is transparent and reproducible. Banker's rounding would give 90 for 89.5 and 88 for 87.5, which many families find surprising because the .5 cases alternate. Half-up matches the common expectation that .5 rounds up, while the one-decimal display lets a parent verify the boundary. The policy should be published in the student handbook and tested in the script before the next term.