Project: Bill splitter
Time to put the whole module together. You'll build a program that splits a café or restaurant bill: it asks for the total in birr, the tip, and the number of people, then prints a tidy receipt with each person's share. You'll build it in three steps, running it after each one, then take on a small money puzzle that real payment apps have to solve too.
Here's what the finished program prints when the bill is 1,850 birr, the tip is 10%, and three people share it:
Bill total in birr: 1850
Tip percentage: 10
Number of people: 3
Bill 1,850.00 birr
Tip 185.00 birr
Grand total 2,035.00 birr
Each person 678.33 birrWork through each milestone in its code box. Try it yourself before you open the solution; getting stuck and then unstuck is where the learning happens.
Milestone 1: the arithmetic
Start without any questions: give the values names and get the arithmetic right. The
tip is a percentage, so 10% of the bill is bill * 10 / 100, not bill * 10.
Exercise
Work out the split
The names are ready. Add three names, tip, grand_total, and share, and print
them like this. Don't worry about the long decimals yet.
Your code
bill = 1850
tip_percent = 10
people = 3
# Work out the tip, the grand total, and each person's shareIn the editor, press Escape then Tab to move on.
Show a solutionHide the solution
This is one way to solve it, not the only one. If yours prints the same thing, it works.
bill = 1850
tip_percent = 10
people = 3
tip = bill * tip_percent / 100
grand_total = bill + tip
share = grand_total / people
print("Tip:", tip)
print("Grand total:", grand_total)
print("Each person:", share)Two things to notice. The tip is 185.0, a float, because / always gives one. And
the share has far more decimals than any birr amount can. Milestone 3 fixes that.
Milestone 2: asking the user
Now replace the three fixed values with questions. Remember lesson 1.5: whatever the user types arrives as text. Before you run it, think about what would happen if you forgot to convert.
Exercise
Ask for the numbers
Replace the first three lines with input() calls. Use float() for the bill and
the tip, since they can have decimals, and int() for the number of people. The
Input box already has 1850, 10, and 3.
Your code
bill = 1850
tip_percent = 10
people = 3
tip = bill * tip_percent / 100
grand_total = bill + tip
share = grand_total / people
print("Tip:", tip)
print("Grand total:", grand_total)
print("Each person:", share)In the editor, press Escape then Tab to move on.
One answer per line, given to input() in order.
Show a solutionHide the solution
This is one way to solve it, not the only one. If yours prints the same thing, it works.
bill = float(input("Bill total in birr: "))
tip_percent = float(input("Tip percentage: "))
people = int(input("Number of people: "))
tip = bill * tip_percent / 100
grand_total = bill + tip
share = grand_total / people
print("Tip:", tip)
print("Grand total:", grand_total)
print("Each person:", share)If you forgot a conversion, you met a TypeError: Python won't multiply or divide
text by a number. The error points at the line that did the arithmetic, not the line
that forgot to convert, so when you see one, check where each value came from.
Milestone 3: the receipt
Last step: a receipt people would trust. Use what you learned in lesson 1.4: labels in
a column 14 characters wide, amounts 12 wide on the right, with thousands separators
and two decimals (:>12,.2f). A blank print() separates the questions from the
receipt.
Exercise
Format the receipt
Replace the three print lines at the end so the output matches the receipt at the
top of this page exactly. The note under the code box tells you when it does.
Your code
bill = float(input("Bill total in birr: "))
tip_percent = float(input("Tip percentage: "))
people = int(input("Number of people: "))
tip = bill * tip_percent / 100
grand_total = bill + tip
share = grand_total / people
print("Tip:", tip)
print("Grand total:", grand_total)
print("Each person:", share)In the editor, press Escape then Tab to move on.
One answer per line, given to input() in order.
Show a solutionHide the solution
This is one way to solve it, not the only one. If yours prints the same thing, it works.
bill = float(input("Bill total in birr: "))
tip_percent = float(input("Tip percentage: "))
people = int(input("Number of people: "))
tip = bill * tip_percent / 100
grand_total = bill + tip
share = grand_total / people
print()
print(f"{'Bill':<14}{bill:>12,.2f} birr")
print(f"{'Tip':<14}{tip:>12,.2f} birr")
print(f"{'Grand total':<14}{grand_total:>12,.2f} birr")
print(f"{'Each person':<14}{share:>12,.2f} birr")Test it
A program that works for one input might not work for others. Put your finished program through these two tests as well: change the lines in the Input box, run it, and compare with the output here, character by character.
Test 2 is a big dinner: 12,500 birr, a 15% tip, four people.
Bill total in birr: 12500
Tip percentage: 15
Number of people: 4
Bill 12,500.00 birr
Tip 1,875.00 birr
Grand total 14,375.00 birr
Each person 3,593.75 birrTest 3 is 1,000 birr, no tip, three people.
Bill total in birr: 1000
Tip percentage: 0
Number of people: 3
Bill 1,000.00 birr
Tip 0.00 birr
Grand total 1,000.00 birr
Each person 333.33 birrMilestone 4: the missing santim
Look closely at test 3. Each of three people pays 333.33 birr. Add it up.
What will this print?
Decide before you look. Guessing wrong is how this sticks.
share = round(1000 / 3, 2)
print(f"{share * 3:.2f}")Python prints
999.99
Three people paying 333.33 birr each is 999.99 birr, one santim short of the bill. Rounding each share to two decimals threw away a third of a santim three times.
One santim is small, but a payment app that loses a santim on every split bill loses
real money over millions of payments. The fix is the rule from lesson 1.2: count
money in whole santim. Divide with // so everyone pays the same whole number of
santim, and % tells you exactly how many are left over. Step through it:
Step through it
Press Next to run one line at a time and watch each name change.Each row is one step: the line that just ran, and every name's value after it.
| Name | Value after line 1 |
|---|---|
| grand_total | 1000.0new |
| people | not made yet |
| total_santim | not made yet |
| share_santim | not made yet |
| leftover | not made yet |
Step 1 of 5
Step 1 of 5. Line 1: grand_total = 1000.00. grand_total is now 1000.0.
| Step | Line | grand_total | people | total_santim | share_santim | leftover |
|---|---|---|---|---|---|---|
| 1 | 1 | 1000.0 | not made yet | not made yet | not made yet | not made yet |
| 2 | 2 | 1000.0 | 3 | not made yet | not made yet | not made yet |
| 3 | 3 | 1000.0 | 3 | 100000 | not made yet | not made yet |
| 4 | 4 | 1000.0 | 3 | 100000 | 33333 | not made yet |
| 5 | 5 | 1000.0 | 3 | 100000 | 33333 | 1 |
Everyone pays 33,333 santim, and 1 santim is left. Someone pays it, and now the shares add up to exactly 1,000 birr.
Exercise
Share the leftover santim (stretch)
Add the santim calculation to your program, after grand_total is worked out. Print
what everyone pays, and how much extra one person pays so the total is exact. For
test 3 the end of the output should read:
Each person pays 333.33 birr
One person pays 0.01 birr moreYour code
grand_total = 1000.00
people = 3In the editor, press Escape then Tab to move on.
Show a solutionHide the solution
This is one way to solve it, not the only one. If yours prints the same thing, it works.
grand_total = 1000.00
people = 3
total_santim = round(grand_total * 100)
share_santim = total_santim // people
leftover = total_santim % people
print(f"Each person pays {share_santim / 100:,.2f} birr")
print(f"One person pays {leftover / 100:,.2f} birr more")round(grand_total * 100) matters there: a float times 100 can land a hair below the
whole number, and round brings it back before you count.
Key takeaways
- Build in small steps and run after each one: arithmetic first, then input, then formatting.
- Convert every number that comes from
input(); a missing conversion shows up later, where the arithmetic happens. :>12,.2fgives amounts that line up, with thousands separators and two decimals.- Test with more than one input, including awkward ones like a bill that doesn't divide evenly.
- For money, count whole santim with
//and%so nothing goes missing.