Computer Science & Engineering 501
Lab Assignment 2
http://www.cs.wustl.edu/~sck/501/current-semester/lab/lab2/
Lab number 2: Rational Computation
Assigned: January 22, 2008
Demo: February 5, 2008
Due date: February 7, 2008
Objective:
Fractions are very useful when doing calculations
and often are the most natural way to express a quantity.
Under the right circumstances,
they can provide more accurate computations
than floating point arithmetic.
This assignment requires that you implement the functionality of
rational numbers (i.e., fractions) so that this can become
a useful and reusable class in other Java programs.
Once you have completed this assignment,
you will understand how classes are designed and specified in Java.
You will know how to implement and test methods that define
behavior for the objects that you have created.
You will appreciate the difference between mutable and immutable classes.
You will have written your first sizeable Java program
and begin to understand the concept of object orientation.
Preparation:
-
Portions of Ch. 4 of Core Java
which discusses the creation and instantiation of classes in Java
are relevant to this assignment.
-
Portions of
the Java tutorial on Classes
are relevant to this assignment.
-
Portions of
the wikipedia discussion on Fractions
may be useful in reviewing the operations of fractions.
-
Review class notes and examples.
Some hints for how best to perform labs are always given in class.
-
Review the
Style Guide
to make sure you are not violating any of the style principles that
make programs better organized and easier to read.
Assignment:
Develop a rational number package,
which contains an immutable class named Rational,
to support manipulation of fractional values.
You should include the following methods in your implementation:
- Constructors:
- Default (no argument) constructor --
constructs a Rational with a zero value
- Constructor with one int argument --
constructs a Rational whose value is equivalent to the int.
- Constructor with two int arguments --
constructs a Rational with the given numerator and denominator.
- Accessor methods:
- getNumerator() returns an int
which is the value of the numerator.
- getDenominator() returns an int
which is the value of the denominator.
- Mutator methods:
- There are no mutator methods since the class is immutable.
- Rational-valued arithmetic methods:
- add(Rational rat) returns a Rational
whose value is the sum this+rat
- subtract(Rational rat) returns a Rational
whose value is the difference this-rat
- multiply(Rational rat) returns a Rational
whose value is the product this*rat
- divide(Rational rat) returns a Rational
whose value is the quotient this/rat
- invert() returns a Rational
whose value is the quotient 1/this
- negate() returns a Rational
whose value is -this
- abs() returns the absolute value of this
- pow(int exp) returns a Rational
whose value is thisexp
- Conversion methods:
- toString() overrides inherited toString() method;
returns a String that best expresses the number.
- floatValue() returns a float
whose value is equivalent to this.
- doubleValue() returns a double
whose value is equivalent to this.
- intValue()(integer division) returns an int
whose value is equivalent to this.
- ceiling() returns the nearest int
which is greater than or equal to the value of this.
- floor() returns the nearest int
which is less than or equal to the value of this.
- Boolean-valued methods:
- isInteger() returns a boolean which is
true iff the denominator divides into the numerator
- isImproper() returns a boolean which is
true iff the numerator is larger than the denominator.
- equals(Rational rat) returns a boolean which is
true iff this is equivalent to rat.
- notEquals(Rational rat) returns a boolean which is
true iff this is not equivalent to rat.
- lessThan(Rational rat) returns a boolean which is
true iff this is less than rat.
- lessThanOrEqual(Rational rat) returns a boolean which is
true iff this is less than or equal to rat.
- greaterThan(Rational rat) returns a boolean which is
true iff this is greater than rat.
- greaterThanOrEqual(Rational rat) returns a boolean which is
true if this is greater than or equal to rat.
- Private comparison
- compareTo(Rational rat) which returns:
- -1 if this is less than rat
- 0 if this is equal to rat
- 1 if this is greater than rat
- Private utility method:
- normalize() -
puts fraction into canonical form with sign in numerator
(see "extras").
Computation:
The proper formulas to use should be obvious to a great extent,
but some of them will be discussed in class.
The numerator and denominator values are stored
in two integer variables.
Use accessor methods to access those values.
Hints:
Do not try to do define all of the methods at once.
Here are some ideas:
- Define the two instance variables,
numerator and denominator.
- Define the two accessor methods,
getNumerator and getDenominator.
- Start with a simple version of the toString() method
so that you can print out Rationals properly.
A simple line like:
return getNumerator() + "/" + getDenominator();
will work fine.
- Implement some or all of the constructors.
Use normalize to place negation in the proper place.
- Write a simple test that creates some rationals and prints them out.
Verify that things are working as expected.
- Implement subtract in terms of
add and negate.
- Test and Verify.
- Implement divide in terms of
multiply and invert.
- Test and Verify.
- Implement isInteger using modulus (%).
- Test and Verify.
- Implement isImproper.
A rational is improper if the absolute value of the numerator
is greater than the absolute value of the denominator.
- Test and Verify.
- Implement the rest of the boolean-valued methods in terms of
compareTo.
- Test and Verify.
- Try to reuse code as much as possible.
- Use accessor methods,
getNumerator() and getDenominator(),
to implement things instead of accessing the values directly.
This is simply good practice since it allows
the underlying representation to be easily modified
without re-thinking all your code.
Extras:
To receive a maximum score out of 80%, you may simply complete
the assignment as described above.
To be eligible to receive a score out of 100%,
you must complete at least some of the following extra parts.
The quality and quantity of the extra work will determine how many
points are awarded.
- (Easy, but strongly recommended)
The greatest common divisor of integers x and y
is the largest integer that evenly divides both
x and y.
Add greatest common divisor gcd(int, int)
as a private utility method and
use it within normalize() to reduce each fraction to its lowest terms.
Note: There are several algorithms for doing this.
One method allows a recursive definition:
Assume x and y are two non-negative arguments and
% is the modulus operator.
If y is equal to 0, then
gcd( x, y ) is x;
otherwise
gcd( x, y ) is gcd( y, x % y ).
- (Easy)
If you are using Eclipse to do your work, you may wish to place your
test cases in a separate JUnit Test Case which can be created
within your project by selecting it on the New pulldown menu.
- (Easy)
Create a package out of your code.
If you are using an IDE such as Eclipse,
you will need to follow the steps for creating a package
under the New menu and by creating the class under that package.
If you are compiling and executing your programs from the command line
as in unix/linux, follow these steps:
- Decide on a name for your package and place a package declaration
package Foo;
as the first statement in your Rational number implementation.
- Create a subdirectory using the same name as your package
and place your Rational.java file in that
subdirectory.
- Insert the statement:
import Foo.*;
in any application that needs access to the Rational class.
- Recompile everything while in the parent directory.
Some further information about packages will be given in class.
One further suggestion: define a main procedure within the
Rational class definition.
This makes the Rational.class file executable and will forever
carry the test cases along with the code for rational numbers.
If the file ever needs to be changed for any reason, it can easily be
re-tested to verify that all test cases still work.
- (Moderate to Difficult)
Add a constructor that takes a String argument containing
a Rational number in string form.
- (Moderate to Difficult)
In your original implementation, you may have silently ignored
a denominator of zero and ran into problems later.
Or perhaps in your original implementation, you simply issued a
warning message and continued on.
This is generally a bad approach for production code.
Decide how your package will react to a denominator of zero.
Choose an approach and implement it.
Here are some suggestions:
- You could throw an exception (ArithmeticException) and expect the
client program to arrange for the try..catch.
- You could silently store the fraction containing a zero denominator
as 1/0
and modify your implementation to specifically test for zero in the
denominator position wherever it may affect the outcome.
One way to convert a fraction with a zero denominator to a String
might be to return the string "infinity" or "negative infinity."
Other options might be to define it as "NaN"
(see not-a-number),
or simply "undefined".
- (Moderate to Difficult)
Use
BigInteger
instead of int to implement numerator and denominator, as these can get quite large.
Also include a conversion function
toBigDecimal(int scale)
which produces a
BigDecimal
value with indicated scale.
You may decide to make this a separate class called BigRational
so that the user can choose between the two.
-
Other ideas are encouraged.
If you have an idea for a modification to this class which you think will
enhance it in some way, please discuss this with the instructors.
Note about extras:
We encourage you to attempt at least the easier problems.
Points assigned for implementing extras are completely at our discretion.
However,
you do NOT have to implement ALL of these suggestions to earn maximum points.
Both the quality and quantity of extra work will be taken into consideration.
Trust us to make reasonable decisions in this regard.
Stan Kwasny (sck@wustl.edu)