LESSON 14 · PRACTICE PROJECT19 min

Project: Tip Calculator

Combine functions, arithmetic, and formatted output to split a restaurant bill.

Video coming to YouTube

Work through the full lesson and run its code while the video is being prepared for publication.

Understand the concept

A small project turns separate language features into one useful result. Compute the tip as bill × percent ÷ 100. Add it to the bill, divide by the number of people, then round the per-person amount to two decimal places.

Put the calculation in a function so the same code works for different bills. Keep numeric calculation separate from display formatting: the function returns a number, while an f-string prints a dollar amount with exactly two digits after the decimal point.

This simplified example uses floating-point numbers. Money applications with strict accounting rules usually use decimal.Decimal; that is a later topic. Assume the number of people is positive in this project.

  • Percent means divide by 100
  • A function can return the per-person amount
  • {amount:.2f} formats two decimal places

See it step by step

Read the code, predict the output, then compare it with the result.

01. Find a tip

PYTHON
bill = 50
tip = bill * 10 / 100
print(tip)
EXPECTED OUTPUT
5.0

Ten percent is ten divided by one hundred, so the tip on 50 is 5.

02. Split a tipped bill

PYTHON
bill = 30
tip = bill * 20 / 100
each = (bill + tip) / 3
print(each)
EXPECTED OUTPUT
12.0

A 20% tip adds 6; the total 36 divided among three people is 12 each.

03. Format the price

PYTHON
amount = 12
print(f"Pay ${amount:.2f}")
EXPECTED OUTPUT
Pay $12.00

The format specifier .2f displays a numeric amount with two decimal places.

A closer look

Follow the reasoning, inspect each result, then try the suggested changes in the console below.

01 / 03

Decide what the numbers mean

Before writing arithmetic, define the agreement at the table. Is the percentage applied to the food subtotal or to a total that already includes tax? Is the service charge already included? Python will faithfully calculate either interpretation, so the input names and the order of operations matter more than clever syntax. This example treats bill as the amount before the tip, with no separate tax or service charge.

A useful way to check the formula is to calculate the total first. For a bill of 54 and an 18 percent tip, 18/100 is 0.18, so the tip is 9.72. The group owes 63.72, and three equal shares are 21.24. Write those intermediate values down when debugging; a believable looking final number can still come from a wrong percentage base.

PYTHON
bill = 54.00
tip_rate = 18 / 100
tip = bill * tip_rate
total = bill + tip
share = total / 3
print(f"Tip: {tip:.2f}")
print(f"Total: {total:.2f}")
print(f"Each: {share:.2f}")
EXPECTED OUTPUT
Tip: 9.72
Total: 63.72
Each: 21.24
Follow the reasoning
  1. Convert 18 percent to 0.18 before multiplying the bill.
  2. Add the tip once, then divide the complete total among three people.
  3. Predict the share if the tip changes to 0 percent, then change the code to check.
02 / 03

Who pays the leftover cent?

Displaying each share to two decimal places does not guarantee that all displayed shares add back to the exact bill. Ten dollars split three ways gives 3.33 each when independently rounded, which totals 9.99. For casual estimates that may be acceptable; for a payment screen, one person needs to cover the remaining cent. Think in whole cents when the sum must reconcile.

The built-in divmod function gives both the common share and the remainder. Start with an integer number of cents, assign everyone the quotient, then give one extra cent to the first remainder people. The chosen ordering is a policy, not a mathematical necessity. This is a small example of why money software eventually needs an explicit rounding and allocation rule.

PYTHON
total_cents = 1000
people = 3
base, extra = divmod(total_cents, people)
shares = [base + (1 if person < extra else 0)
          for person in range(people)]
print(shares)
print(sum(shares) == total_cents)
EXPECTED OUTPUT
[334, 333, 333]
True
Follow the reasoning
  1. divmod(1000, 3) produces 333 whole cents per person and one leftover cent.
  2. Only the first share receives the extra cent, so the sum remains 1000.
  3. Change total_cents to 1001 and predict which shares increase.
03 / 03

Reject inputs that make no sense

The core calculation assumes a positive number of people and nonnegative bill and tip values. If people is zero, division fails; if people is negative, the answer is numeric but useless. Validate these conditions at the boundary where values enter the program. A helpful error tells the caller what must change and keeps the actual calculation simple.

A function can raise ValueError for impossible inputs while still returning a number for valid ones. The caller decides whether to show that error to a person, ask for another entry, or log it. For this exercise, catch the error only around the demonstration call. Try changing the group size from zero to two and predict whether the except branch will run.

PYTHON
def per_person(total, people):
    if total < 0:
        raise ValueError("Total cannot be negative")
    if people <= 0:
        raise ValueError("People must be positive")
    return total / people

try:
    print(per_person(36, 0))
except ValueError as error:
    print(error)
print(per_person(36, 3))
EXPECTED OUTPUT
People must be positive
12.0
Follow the reasoning
  1. Check the input before any division occurs.
  2. A named ValueError is caught by the caller, while the valid call returns 12.0.
  3. Replace zero with two to see the successful path.

Try it in Python

Edit the example and run it. Python starts in your browser the first time you click Run.

Python console

Ready to run
Need input()? Add one value per line
Ctrl / ⌘ + Enter to run
OUTPUT
Your output appears here.
Your turnComplete split_bill(bill, tip_percent, people). Calculate the tip, add it to the bill, divide by people, and return the per-person amount rounded to two decimal places. Keep the print line and make it display Each person pays $12.00 for the prepared values.
Need a hint?

Try tip = bill * tip_percent / 100, then total = bill + tip. Divide total by people and return round(result, 2). The f-string handles display only.

Complete the task and select Check task to verify your code.

    Quick quiz

    Three questions. You can change your answers and try again.

    01.What is a 15% tip on a bill of 20?

    02.Why should split_bill return a number instead of a formatted sentence?

    03.What does :.2f do in an f-string?

    Typical mistakes

    Everyone meets these errors. See what causes them and how to fix them.

    Using the percent as a multiplier

    COMMON MISTAKE
    tip = bill * 20
    FIX
    tip = bill * 20 / 100

    What happens: A 20% tip on 40 becomes 800 instead of 8.
    Convert a percent to a fraction before multiplying.

    Dividing before adding the tip

    COMMON MISTAKE
    each = bill / people + tip
    FIX
    each = (bill + tip) / people

    What happens: The whole tip is charged to every person.
    Add the tip once to the whole bill, then split the total.

    Sources and further reading