patternjavaMinor
"Rational" Wrapper class with support for Rational Number Approximation
Viewed 0 times
numberapproximationwithrationalwrapperforclasssupport
Problem
Enough with imprecise floating point datatypes called
Rational.java
```
/**
* This class encapsulates a Rational number. Any rational number
* {@code R} can be represented as a quotient of two whole numbers,
* {@code p}, and {@code q}. Provided a decimal number,
* this class can approximate the numerator and denominator to precision of
* 1E-11 (the default). It is also a handy implementation for approximating
* irrational values like that of {@link java.lang.Math#PI Math.PI},
* or {@link java.lang.Math#E Math.E}, or the golden ratio: φ.
*
* @author Subhomoy Haldar (ambigram_maker)
* @version 1.1
*/
public class Rational {
/**
* A public constant that defines the rational value of 0.
*/
public static final Rational ZERO = new Rational(0, 1);
/**
* A public constant that defines the rational value of 1.
*/
public static final Rational ONE = new Rational(1, 1);
/**
* A public constant that defines the rational value of 0.5.
*/
public static final Rational HALF = new Rational(1, 2);
/*
* The Lowest and the Highest levels of precision allowed.
*/
private static final double LWST_PREC = 1E-3;
private static final double HIST_PREC = 1E-16;
private static double precision = 1E-10;
private final long num;
private final long den;
private final double result;
/**
* Creates a new {@code Rational} with the given numerator and
* denominator. It has special cases for Positive Infinity
* ({@link java.lang.Double#POSITIVE_INFINITY Double.POSITIVE_INFINITY}),
* Negative Infinity ({@link java.lang.Double#NE
float anddouble. I present to you Rational. Of course, Rational number arithmetic is easy, so I took up a much more interesting challenge: Rational Number Approximation. This is what I have implemented in my code. I have employed a technique similar to binary search, the only difference being that I am the mediant in place of the middle element.Rational.java
```
/**
* This class encapsulates a Rational number. Any rational number
* {@code R} can be represented as a quotient of two whole numbers,
* {@code p}, and {@code q}. Provided a decimal number,
* this class can approximate the numerator and denominator to precision of
* 1E-11 (the default). It is also a handy implementation for approximating
* irrational values like that of {@link java.lang.Math#PI Math.PI},
* or {@link java.lang.Math#E Math.E}, or the golden ratio: φ.
*
* @author Subhomoy Haldar (ambigram_maker)
* @version 1.1
*/
public class Rational {
/**
* A public constant that defines the rational value of 0.
*/
public static final Rational ZERO = new Rational(0, 1);
/**
* A public constant that defines the rational value of 1.
*/
public static final Rational ONE = new Rational(1, 1);
/**
* A public constant that defines the rational value of 0.5.
*/
public static final Rational HALF = new Rational(1, 2);
/*
* The Lowest and the Highest levels of precision allowed.
*/
private static final double LWST_PREC = 1E-3;
private static final double HIST_PREC = 1E-16;
private static double precision = 1E-10;
private final long num;
private final long den;
private final double result;
/**
* Creates a new {@code Rational} with the given numerator and
* denominator. It has special cases for Positive Infinity
* ({@link java.lang.Double#POSITIVE_INFINITY Double.POSITIVE_INFINITY}),
* Negative Infinity ({@link java.lang.Double#NE
Solution
Creating a
Does it really make sense for a
It seems to me that a factory method would be more interesting for this purpose,
for example:
Notice the added advantage that this technique can reuse logic in the canonical constructor.
Single responsibility principle
The
Some of these operations fit nicely in the
Some sound like they could be elsewhere.
I suggest moving the calculations outside to a different class.
They are not essential for the functioning of
and weaken the cohesion of the other methods of the class.
Unit testing
It's great that you have unit tests.
But I recommend using JUnit4 instead of JUnit3.
Changing is as simple as adding
and changing the imports.
Naming
The constructor takes
but inside the class you're using
I suggest to spell out properly
Similarly:
Although this looks comic ("nude", chuckles), I suggest to find better names.
Finally,
What sense does it make for a
Negating numbers
You're doing
Simply:
Rational from a doubleDoes it really make sense for a
Rational class to have a constructor that takes a double?It seems to me that a factory method would be more interesting for this purpose,
for example:
public static Rational fromDouble(double decimal) {
if (decimal == Double.NaN) {
return new Rational(0, 0);
}
if (decimal == Double.POSITIVE_INFINITY) {
return new Rational(1, 0);
}
if (decimal == Double.NEGATIVE_INFINITY) {
return new Rational(-1, 0);
}
// ...Notice the added advantage that this technique can reuse logic in the canonical constructor.
Single responsibility principle
The
Rational class does these things:- Represent a rational number composed of numerator and denominator
- Normalize the sign of numerator and denominator to canonical form (keep sign in numerator)
- Perform operations on rational numbers: add, subtract and others
- Calculate an approximation to a
double
- Calculate highest common factor
- ...
Some of these operations fit nicely in the
Rational class.Some sound like they could be elsewhere.
I suggest moving the calculations outside to a different class.
They are not essential for the functioning of
Rational,and weaken the cohesion of the other methods of the class.
Unit testing
It's great that you have unit tests.
But I recommend using JUnit4 instead of JUnit3.
Changing is as simple as adding
@Test annotation for the test cases,and changing the imports.
Naming
The constructor takes
numerator and denominator,but inside the class you're using
num and den.I suggest to spell out properly
numerator and denominator inside the class too.Similarly:
long nu, de;Although this looks comic ("nude", chuckles), I suggest to find better names.
Finally,
result is not a great name for what it represents.What sense does it make for a
Rational to have a "result"?doubleValue or value would be better.Negating numbers
You're doing
0L - something or 0D - something a lot. Why not simply -something? For example instead of this:numerator = 0L - numerator;
denominator = 0L - denominator;Simply:
numerator = -numerator;
denominator = -denominator;Code Snippets
public static Rational fromDouble(double decimal) {
if (decimal == Double.NaN) {
return new Rational(0, 0);
}
if (decimal == Double.POSITIVE_INFINITY) {
return new Rational(1, 0);
}
if (decimal == Double.NEGATIVE_INFINITY) {
return new Rational(-1, 0);
}
// ...long nu, de;numerator = 0L - numerator;
denominator = 0L - denominator;numerator = -numerator;
denominator = -denominator;Context
StackExchange Code Review Q#71851, answer score: 2
Revisions (0)
No revisions yet.