Skip to content

Mini Project 3 — Fraction Type

Difficulty: Beginner · Estimated effort: 3–4 hours · Prerequisites: Module 5 — Operators and Static Members, Module 4 — Encapsulation · Combines: operator overloading, a normalised invariant, const correctness

No complete solution is provided. The design is yours; the checklists are the acceptance criteria.


1. Scenario

A carpentry workshop records measurements as fractions of an inch because that is what their tape measures show and what their clients quote. They add and compare these measurements by hand dozens of times a day, and they want a program that behaves like the arithmetic they already know: 1/2 + 1/3 should print 5/6, not 0.833333.


2. Problem statement

Build a Fraction type that stores a numerator and a denominator, keeps itself in lowest terms with a positive denominator at all times, supports the arithmetic operators, and compares correctly. Division by zero must be impossible, not merely unlikely.

The invariant — reduced, denominator positive, denominator non-zero — is the whole design problem. If it holds after every operation, everything else falls out.


3. User requirements

  • [ ] Add, subtract, multiply and divide fractions and get a reduced result.
  • [ ] Compare two fractions for equality and ordering.
  • [ ] Print a fraction readably, including a whole number when that is what it is.
  • [ ] Never see a denominator of zero, and never see 2/4 where 1/2 was meant.
  • [ ] Convert to double when a decimal is genuinely wanted.

4. Functional requirements

  • [ ] Construct from two integers, or from one integer (a whole number).
  • [ ] +, -, *, / as free functions so both arguments convert identically.
  • [ ] ==, !=, <, > and <= that are correct whether or not denominators are equal.
  • [ ] operator<< printing the reduced form.
  • [ ] A constructor that refuses a zero denominator with a message naming it.
  • [ ] An explicit conversion to double.

5. Non-functional requirements

  • [ ] Integer-overflow behaviour considered and written down, not hoped for.
  • [ ] No public data members; no operation can leave the object unreduced.
  • [ ] Builds warning-free with g++ -std=c++17 -Wall -Wextra -Werror.
  • [ ] The reduction happens in one place — the constructor — not in each operator.
  • [ ] Free operators are declared in the header and defined in the source file.

6. Suggested features

  • Mixed-number output for an improper fraction: 7/4 printed as 1 3/4.
  • Construction from a decimal string, with a documented rounding rule.
  • An abs() and a reciprocal(), both preserving the invariant.
  • A compare helper returning the sign of the difference, with < defined in terms of it.

7. OOP concepts involved

Concept Where it appears
Encapsulation The invariant is enforced in the constructor and nowhere else needs to know
Invariant Reduced, positive denominator, never zero
Operator overloading Arithmetic and comparison, as the language's own operators
Free vs member operators Why + should allow 1 + f, and what that costs
const correctness Every operator that does not change the object is const
explicit Stopping a silent Fraction f = 3; where it was not intended

8. Suggested design considerations

Reduce in the constructor, once. If each operator reduces its own result you have written the same gcd call five times and the invariant is enforced in five places. One funnel is stronger and shorter.

Think about signs before you write gcd. A negative denominator in the reduced form is the classic slip; decide that the sign lives on the numerator and enforce it where the value is set.

Decide the overflow story. 1/1000 * 1/1000 gives tiny denominators, but 1000000000/3 + 1/7 does not. You do not have to solve it — you do have to notice it, and say in the header what the limits are.

A free operator+ buys symmetry. A member operator cannot make 1 + f work without an unwanted implicit conversion of f's left operand. Say why you chose what you chose.


9. Minimum viable version

Fraction with a validating constructor and gcd reduction, the four arithmetic operators, == and <, operator<<, and a main that demonstrates each.


10. Intermediate extension

Add every comparison operator in terms of one compare function, then check that a < b, a == b and a > b are mutually exclusive for a table of pairs including negatives and zero denominators-that-were-refused.


11. Advanced extension

Add mixed-number printing and construction from a decimal string, then write the rounding rule down and test the boundary: the decimal that is exactly a half, and the one just either side of it. Extra credit: make Fraction usable as a key in std::map, and say what the ordering means.


12. Testing checklist

  • [ ] 1/2 + 1/3 prints 5/6
  • [ ] 2/4 prints as 1/2 no matter how it was constructed
  • [ ] A negative over a negative prints as positive
  • [ ] 1/2 == 2/4 is true, and < orders them as equal
  • [ ] Division by zero is impossible — either refused at construction or handled explicitly
  • [ ] 0/5 prints as 0, not as 0/1
  • [ ] Multiplying by 1 returns an equal value
  • [ ] The four operators disagree with a hand-computed table nowhere in twenty cases

13. Documentation checklist

  • [ ] The invariant stated at the top of the header, in one sentence
  • [ ] A note explaining why reduction lives in the constructor
  • [ ] The overflow limits documented, with the largest safe inputs
  • [ ] Each operator's comment saying what it returns, not how it works
  • [ ] The sign convention written down

14. Self-evaluation rubric

Criterion Excellent looks like Weight
Invariant Nothing can produce an unreduced or zero-denominator Fraction 30%
Operator correctness All four operators and all six comparisons verified against hand results 30%
Design placement Reduction and sign handling in exactly one place 15%
Interface Free operators where symmetry needs them; explicit where silent conversion is wrong 10%
Robustness Overflow limits considered and documented 5%
Documentation Invariant, sign convention and limits written down 10%

Related: Mini projects · Project roadmap · All projects · Module 5 — Operators and Static Members