Project 1: Scientific Calculator and Numeric Foundations

A self-contained deep-dive from the canonical Math from Foundations to Machine Learning curriculum.

  • Difficulty: Beginner
  • Time: 8-12 hours
  • Language: Python (alternatives: C, JavaScript, Rust)
  • Prerequisites: Basic Python syntax
  • Source: ML-Math P01

What You Will Build

Build a command-line scientific calculator that accepts expressions such as (3 + 4) * 2^3, sqrt(16) + log(exp(5)), and sin(pi / 2). Instead of delegating expression evaluation to eval, create a tokenizer, a precedence-aware parser, and an evaluator with explicit domain checks. The calculator should distinguish unary minus from subtraction, support right-associative exponentiation, report syntax errors at a useful position, and print results with a documented floating-point tolerance. Include a verification suite that compares ordinary operations with hand-computed cases and selected transcendental results with Python’s math module.

Real World Outcome

Running the tool produces a stable transcript: valid expressions return a number and evaluation trace; malformed expressions return a deterministic syntax diagnostic; sqrt(-1) and log(0) return domain errors rather than crashes. A test report demonstrates correct precedence, nested parentheses, functions, constants, scientific notation, and near-equality behavior such as 0.1 + 0.2 versus 0.3.

Core Question

How do you turn human mathematical notation, with its implicit ordering and domain rules, into an unambiguous sequence of computer operations?

Concepts You Must Understand First

  1. Operator precedence and associativity: why 2^3^2 groups differently from subtraction. See King, C Programming: A Modern Approach, Chapters 4 and 7.
  2. Tokens, stacks, and expression trees: represent structure before computing values. See Aho et al., Compilers, Chapter 2.
  3. Shunting-yard or recursive-descent parsing: convert infix notation into a form that can be evaluated safely. See Sedgewick and Wayne, Algorithms, §4.3.
  4. Function domains and inverse relationships: log, exp, roots, and trigonometric functions. See Stewart, Calculus: Early Transcendentals, Chapter 1.
  5. Floating-point representation: numerical equality needs tolerances, not string or bit equality. See Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, §2.4.

Build Milestones

  1. Tokenize integers, decimals, parentheses, constants, and + - * / ^, rejecting unknown characters.
  2. Parse arithmetic with correct precedence, associativity, nested parentheses, and unary signs.
  3. Add sqrt, sin, cos, log, and exp with arity and domain validation.
  4. Add deterministic formatting, error categories, and a trace or postfix/AST inspection mode.
  5. Test normal, boundary, malformed, and numerically awkward expressions against trusted results.

Hints in Layers

  1. Start by converting infix tokens to postfix with an operator stack; postfix evaluation needs only a value stack.
  2. Store precedence, associativity, and arity as data rather than spreading special cases through the parser.
  3. Separate the unrounded computed value from display formatting, and centralize approximate comparison in one helper.

Common Pitfalls and Debugging

  • Symptom: -2^2 returns the wrong convention. Cause: unary minus was assigned an accidental precedence. Fix: specify and test the grammar explicitly, including (-2)^2.
  • Symptom: nested functions leave extra values on the stack. Cause: argument boundaries or function arity are not represented. Fix: validate the final stack size and trace tokens one at a time.
  • Symptom: tests around decimals fail intermittently. Cause: exact floating-point equality. Fix: compare absolute/relative error with a justified tolerance.

Definition of Done

  • Arithmetic precedence, associativity, unary signs, and nested parentheses are correct.
  • At least five scientific functions enforce their domains and argument counts.
  • Invalid syntax produces stable, specific errors without executing arbitrary code.
  • Tests cover hand-computed examples, edge cases, and trusted-library comparisons.
  • The README documents grammar, numeric tolerance, limitations, and one failure case.

Complete learning path · Next: Project 2