Tier 0 — Absolute Beginner

Tip Calculator

Calculate tip and split a bill — pure functions, rounding, and Intl.NumberFormat for currency display.

🐍 Python 🟨 JavaScript

1. The Problem

Build a tip calculator: take a bill amount, tip percentage, and number of people — output the tip amount, total bill, and per-person share. Format all amounts as currency using Intl.NumberFormat.

Why this project? Pure arithmetic functions, floating-point rounding with toFixed, and locale-aware currency formatting — fundamentals that appear in every financial calculation.

2. How to Think About This

Three pure functions: calcTip(bill, pct), calcTotal(bill, tip), splitBill(total, people). Pure means no I/O — only input → output. The CLI layer calls them and formats the results. Easy to test, easy to reuse.

3. The Build

tip.mjs
export function calcTip(bill, tipPct) {
  if (bill < 0)          throw new RangeError("Bill cannot be negative");
  if (tipPct < 0 || tipPct > 100) throw new RangeError("Tip % must be 0–100");
  return +(bill * tipPct / 100).toFixed(2);
}

export function calcTotal(bill, tip) { return +(bill + tip).toFixed(2); }

export function splitBill(total, people) {
  if (!Number.isInteger(people) || people < 1)
    throw new RangeError("People must be a positive integer");
  return +(total / people).toFixed(2);
}

export function formatCurrency(amount, locale = "en-IN", currency = "INR") {
  return new Intl.NumberFormat(locale, { style: "currency", currency }).format(amount);
}

// CLI
const [,, billStr, tipStr = "18", peopleStr = "1"] = process.argv;
if (!billStr) { console.error("Usage: node tip.mjs <bill> [tip%] [people]"); process.exit(1); }

const bill   = parseFloat(billStr);
const tipPct = parseFloat(tipStr);
const people = parseInt(peopleStr, 10);

if (isNaN(bill) || bill < 0) { console.error("Invalid bill amount"); process.exit(1); }

const tip       = calcTip(bill, tipPct);
const total     = calcTotal(bill, tip);
const perPerson = splitBill(total, people);
const fmt       = formatCurrency;

console.log(`
Bill:        ${fmt(bill)}`);
console.log(`Tip (${tipPct}%):  ${fmt(tip)}`);
console.log(`Total:       ${fmt(total)}`);
if (people > 1) console.log(`Per person:  ${fmt(perPerson)}  (÷${people})`);

4. Test & Prove

import { calcTip, calcTotal, splitBill } from "./tip.mjs";
let p=0,f=0;
function test(d,fn){try{fn();console.log("  ✓",d);p++;}catch(e){console.log("  ✗",d,"—",e.message);f++;}}
test("18% tip on 100",   () => { if(calcTip(100,18)!==18) throw new Error(calcTip(100,18)); });
test("10% tip on 55",    () => { if(calcTip(55,10)!==5.5) throw new Error(); });
test("total 100+18=118", () => { if(calcTotal(100,18)!==118) throw new Error(); });
test("split 120 by 3",   () => { if(splitBill(120,3)!==40) throw new Error(); });
test("split 100 by 3",   () => { if(splitBill(100,3)!==33.33) throw new Error(splitBill(100,3)); });
test("negative bill",    () => { try{calcTip(-1,10);throw new Error("no");}catch(e){if(e.message==="no")throw e;} });
test("tip > 100 throws", () => { try{calcTip(100,200);throw new Error("no");}catch(e){if(e.message==="no")throw e;} });
test("0 people throws",  () => { try{splitBill(100,0);throw new Error("no");}catch(e){if(e.message==="no")throw e;} });
console.log(`
${p} passed, ${f} failed`);

5. The Interface

node tip.mjs 1200          # ₹1,200 bill, 18% tip, 1 person
node tip.mjs 1200 20 4    # ₹1,200 bill, 20% tip, split 4 ways

Bill:        ₹1,200.00
Tip (20%):   ₹240.00
Total:       ₹1,440.00
Per person:  ₹360.00  (÷4)

6. Run It

node tip.mjs 1200 20 4
node tip.test.mjs

🎯 Try this next

  1. Add custom rounding options — always round up to the nearest whole amount
  2. Add a --currency flag: node tip.mjs 50 18 2 --currency USD
  3. Build a browser version with live calculation as the user types
  4. Add split suggestions — show 15%, 18%, and 20% tip options at once

What you practised

Pure functions for arithmetic, toFixed(2) + unary + to round cleanly, Intl.NumberFormat for locale-aware currency, parseFloat/parseInt from CLI args, and defensive validation with RangeError.

Reference: Numbers & Math · Date & Intl

Try It Yourself