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/4where1/2was meant. - [ ] Convert to
doublewhen 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/4printed as1 3/4. - Construction from a decimal string, with a documented rounding rule.
- An
abs()and areciprocal(), both preserving the invariant. - A
comparehelper 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/3prints5/6 - [ ]
2/4prints as1/2no matter how it was constructed - [ ] A negative over a negative prints as positive
- [ ]
1/2 == 2/4is true, and<orders them as equal - [ ] Division by zero is impossible — either refused at construction or handled explicitly
- [ ]
0/5prints as0, not as0/1 - [ ] Multiplying by
1returns 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