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
- Operator precedence and associativity: why
2^3^2groups differently from subtraction. See King, C Programming: A Modern Approach, Chapters 4 and 7. - Tokens, stacks, and expression trees: represent structure before computing values. See Aho et al., Compilers, Chapter 2.
- Shunting-yard or recursive-descent parsing: convert infix notation into a form that can be evaluated safely. See Sedgewick and Wayne, Algorithms, §4.3.
- Function domains and inverse relationships:
log,exp, roots, and trigonometric functions. See Stewart, Calculus: Early Transcendentals, Chapter 1. - 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
- Tokenize integers, decimals, parentheses, constants, and
+ - * / ^, rejecting unknown characters. - Parse arithmetic with correct precedence, associativity, nested parentheses, and unary signs.
- Add
sqrt,sin,cos,log, andexpwith arity and domain validation. - Add deterministic formatting, error categories, and a trace or postfix/AST inspection mode.
- Test normal, boundary, malformed, and numerically awkward expressions against trusted results.
Hints in Layers
- Start by converting infix tokens to postfix with an operator stack; postfix evaluation needs only a value stack.
- Store precedence, associativity, and arity as data rather than spreading special cases through the parser.
- Separate the unrounded computed value from display formatting, and centralize approximate comparison in one helper.
Common Pitfalls and Debugging
- Symptom:
-2^2returns 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.