Python Coding Exercise #19: Loyalty Bonus
Part of the series Python Practice Exercise — part 19
Before you start: Know input() and int() · Know if and else
The question
This program calculates the end of year bonus a customer receives on their loyalty card. The bonus is calculated with the following formula:
- If there are less than a hundred points on the card, the bonus is 10 %
- In any other case the bonus is 15 %
The program should work like this:
How many points are on your card? 55
Your bonus is 10 %
You now have 60.5 points
But there is a problem with the program, so with some inputs it doesn’t work quite right:
How many points are on your card? 95
Your bonus is 10 %
Your bonus is 15 %
You now have 120.175 points
Please fix the program so that there is always either a 10 % or a 15 % bonus, but never both.
Here is the code to fix:
points = int(input("How many points are on your card? "))
if points < 100:
points *= 1.1
print("Your bonus is 10 %")
if points >= 100:
points *= 1.15
print("Your bonus is 15 %")
print("You now have", points, "points")
Why both bonuses appear
At first glance the two if statements look like opposites, so they shouldn’t both run. And for most inputs they don’t. The trouble starts because the first if changes the variable that the second if checks. Follow what happens with 95 points:
| Step | What Python does | Value of points |
|---|---|---|
| 1 | points = 95 from the input | 95 |
| 2 | First if: is 95 < 100? Yes, so points *= 1.1 and print “10 %“ | 104.5 |
| 3 | Second if: is 104.5 >= 100? Yes, so points *= 1.15 and print “15 %“ | 120.175 |
The second if doesn’t ask about the points the customer started with. It asks about the points right now, and after the 10 % bonus, 95 points have grown to 104.5, which is over 100. So the 15 % bonus is applied on top.
The key idea: an if condition is checked at the moment Python reaches it, using whatever the variable holds at that moment. If an earlier line has changed the variable, the condition sees the new value.
My Solution
Use else, so exactly one of the two branches can run:
points = int(input("How many points are on your card? "))
if points >= 100:
points *= 1.15
print("Your bonus is 15 %")
else:
points *= 1.1
print("Your bonus is 10 %")
print(f"You now have {points} points")
How My Solution Works
An if / else makes a single decision and then picks one branch. Python checks points >= 100 once. If it’s true, the 15 % branch runs and the else is skipped entirely. If it’s false, the else branch runs instead. There’s no second question for the changed points to answer, so the two bonuses can never both apply.
Notice that the condition is points >= 100, with greater than or equal to. That matters: exactly 100 points counts as “any other case”, so it gets the 15 % bonus. (The exercise says the 10 % bonus is only for less than a hundred.)
University Solution
points = int(input("How many points are on your card? "))
if points < 100:
factor = 1.1
print("Your bonus is 10 %")
if points >= 100:
factor = 1.15
print("Your bonus is 15 %")
# a *= b is the same thing as a = a * b
points *= factor
print("You now have", points, "points")
The University Solution still has two separate ifs, so why does it work? Because the ifs no longer change points. They only choose a factor (1.1 or 1.15) and print the message. The multiplication points *= factor happens once, after both ifs. Both conditions therefore look at the original number of points, and exactly one of them is true.
It fixes the bug in a different way: instead of switching to else, it stops the first if from changing the value that the second if reads. Both approaches are correct. The if / else version is shorter and makes it obvious that only one branch can run. The factor version keeps the calculation in one place. (The *= shortcut was covered in Exercise #18.)
About round() and the long decimals
You might expect 60.5 for 55 points, because that’s what the exercise text shows. But if you run the program, Python actually prints a longer number. Here are real results for a few inputs:
| Points | Real output | With round(points, 2) |
|---|---|---|
| 55 | 60.50000000000001 | 60.5 |
| 95 | 104.50000000000001 | 104.5 |
| 100 | 114.99999999999999 | 115.0 |
These tiny errors are floating-point rounding, as in Data Types in Python. In real code, round() is a good way to tidy them up, and it would make the output look much nicer.
For this exercise, though, round() is the wrong choice. The course checks the program’s output exactly, and it expects the full float that Python really produces. Rounding changes the printed text (60.5 instead of 60.50000000000001), so the output no longer matches and the check fails. So the lesson is: when an exercise says the output must match exactly, don’t “improve” the output, even when the improvement is nicer to look at.
Key takeaways
- An
ifcondition uses the value of the variable at the moment it’s checked. If an earlier line changed it, the condition sees the new value. - Two separate
ifs can both run, even when their conditions look like opposites. Useif/elsewhen exactly one branch should run. - Another fix is to make the
ifs only choose a value, and do the calculation once afterwards. >= 100includes 100 itself.< 100doesn’t.- Floats can print with tiny errors.
round()tidies them up, but if an exercise needs the exact output, rounding can make it fail.
✦ This is part of my Python Practice Exercise series, where I work through exercises while learning Python and explain each answer in plain language. ✦
Practice Source: This exercise is taken from the Python Programming MOOC 2026 by the University of Helsinki. I’m working through the exercises as part of my Python learning journey and documenting my answers and explanations here.