Skip to content

Project: Bill splitter

5 min readProject

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:

Output
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 birr

Work 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 share

In the editor, press Escape then Tab to move on.

Show a 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 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 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.

Output
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 birr

Test 3 is 1,000 birr, no tip, three people.

Output
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 birr

Milestone 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.

grand_total = 1000.00
people = 3
total_santim = round(grand_total * 100)
share_santim = total_santim // people
leftover = total_santim % people
Names and their values after line 1
NameValue after line 1
grand_total1000.0new
peoplenot made yet
total_santimnot made yet
share_santimnot made yet
leftovernot made yet

Step 1 of 5

Step 1 of 5. Line 1: grand_total = 1000.00. grand_total is now 1000.0.

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:

Output
Each person pays 333.33 birr
One person pays 0.01 birr more

Your code

grand_total = 1000.00
people = 3

In the editor, press Escape then Tab to move on.

Show a 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,.2f gives 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.