Topical Information

This lab will help you practice with classes and libraries.

Program Information

Design and code a class to represent the set of rational numbers ala mathematics. Recall that rational numbers have a numerator and a denominator both of which come from the set of integers.

Common mathematical operations on rational numbers are addition, subtraction, multiplication, division, and reciprocal:

                   A     B
    Reciprocal of --- = ---
                   B     A

            A       B     A + B
    Sum of --- and --- = -------
            C       C       C

            A       C     A*D + C*B
    Sum of --- and --- = -----------
            B       D        B*D

                   A       B     A - B
    Difference of --- and --- = -------
                   C       C       C

                   A       C     A*D - C*B
    Difference of --- and --- = -----------
                   B       D        B*D

                A       C     A*C
    Product of --- and --- = -----
                B       D     B*D

                 A       C     A     D     A*D
    Quotient of --- and --- = --- * --- = -----
                 B       D     B     C     B*C

                         A       A     -A      A
    Additive Inverse of --- = - --- = ---- = ----
                         B       B      B     -B


    Comparison:

     A     C
    --- < --- --> A*D < C*B
     B     D

     A     C
    --- ≤ --- --> A*D ≤ C*B
     B     D

     A     C
    --- > --- --> A*D > C*B
     B     D

     A     C
    --- ≥ --- --> A*D ≥ C*B
     B     D

     A     C
    --- = --- --> A*D = C*B
     B     D

     A     C
    --- ≠ --- --> A*D ≠ C*B
     B     D

Noting that we need not consider special cases such as 'like denominators' for addition and subtraction, we can throw those two formulas out. We also note the use of both multiplication and reciprocal during division and so we will get to re-use our functionality (should we so desire).

To make sure the sum and difference (and maybe the product and quotient?) do not overflow the integer type we choose, we should make sure to keep the numerator and denominator 'reduced to lowest terms' at all times. This means we will need a helper function to find the least common multiple (LCM; aka the greatest common divisor; GCD) of the numerator and denominator. We could make this a private class function/method... But, due to its generic nature — having nothing specifically to do with rational numbers — we should probably place it in its own library (maybe a math related one?).

(Note: You cannot use either the gcd or lcm functions from the standard library! You must code the greatest common divisor for yourself to receive full credit. Note also the option below for a faster method.)

Display and entry of rational numbers can be deceptively difficult so let's keep it as simple as possible and forgo the standard notation used above in favor of the more basic: A/B. (For further discussion, please see the notes elsewhere on the site.)

To keep the display format as readable as possible, we should keep the denominator positive at all times. (So if we read or calculate a negative denominator somewhere, we should take the additive inverse of both it and the numerator — 'canceling' the common -1 factor from both.) Along with keeping the denominator positive, let's keep it non-zero, too. (It wouldn't do to be dividing by 0, now would it?)

Speaking of dividing, provide a method (asdecimal, decimalform, approximate, or whatever) which returns the decimal representation of the rational number (1/2 --> 0.5).

Noting that formulas are provided for unary negation and comparison/testing, you should provide methods for those operations, too.

Don't forget to include a complete set of constructors, accessors, and mutators in addition to the mathematics specialty functions and input/output mentioned above.

Place your rational ADT in a library.

Write a test application (driver) to make sure ALL of your operations work correctly.

Thought Provoking Questions

  1. Did you choose short or long for your numerator/denominator type? Why is yours the best choice?

  2. How can you keep the denominator positive? What happens to the numerator? Where should this code be placed?

  3. Do you have any private method(s)? If so, what does it/them do? (Hint: *ahem* reduce() *ahem* normalize() *ahem*)

  4. What type of value is returned from your comparison/testing functions? (Hint: In what context are they likely to be used? What [data] type would be appropriate there?)

  5. How many lines are in your less and equal functions? (I bet there aren't many...)

  6. Is there any original code in your methods for greater, greater_equal, less_equal, and/or not_equal? (Hint: Maybe they just re-use some other functions you've already written in a clever way?)

  7. Do your math functions change the calling object or their [single] argument? Should they? (Hint: What about when you code x + y — does the compiler change either x or y?) How do you specify such situations to the compiler so it can better handle/enforce your decision?

  8. How many lines are in your math functions? (Hint: You are constructing a new object to return to the caller...)

  9. Does your input method prompt the user before reading the rational number? Why should it not?

  10. Does your output method print anything besides the rational number (in proper notation, of course) — even an endl? Why should it not?

  11. Why do/can you only have one mutator? Don't we normally have a separate mutator method for each data member of the class? (Hint: Does this relate to the thought-provoking question above? What other things might also influence this decision? What might happen if the programmer could change the numerator but not the denominator? Vice versa? Would this complicate your validation rules?)

This assignment is (Level 5).

Options


Total Level Possible

If you did all above options, this lab could be worth as much as (Level 20).