BigInteger.gdc(BigInteger)
always returns a non-negative value, so the lineif(the_gcd.signum() == -1) the_gcd = the_gcd.abs();
in normalize()
is redundant.
Your
Rational(String)
constructor can be easily tricked into accepting a zero denominator:new Rational("5/00")
A safer way to check a String
for representing an integer with the value of 0 would be to invoke Integer.parseInt(String)
and then compare the returned int
to 0
. This would guard against all sorts of loopholes, including "-0"
. Alternatively, you could first create a BigInteger
from the denominator String
and then compare the resulting BigInteger
to BigInteger.ZERO
.
The
mod()
method is broken: Consider aRational
where the low-order 32 bits of the numerator are0x00000002
and the low-order 32 bits of the denominator are0x00000001
, and all higher-order bit-pairs are equal and at least one bit thereof is1
.mod()
should now return1
, but it actually returns0
. It would also throw anArithmeticException
if the low-order 32 bits of the denominator are all0
, because then an illegal division by zero would be attempted. Your method would be more stable and predictable if you first calculated the modulo as aBigInteger
, and only then, if you really want to return anint
instead of aBigInteger
, convert the result to anint
.Your method
toReal()
checks the denominator for being zero. But thedenominator
of aRational
can/should not be zero in the first place – the constructors already see to that. Ifdenominator
is zero whentoReal()
is invoked, then there is a bug in your code. Instead of throwing aRuntimeException
, there is a language construct specifically designed for this purpose: An assertion:assert !this.denominator.equals(ZERO);
If denominator
equals ZERO
, then an AssertionError
will be thrown. An AssertionError
is also a Throwable
, so the effect is pretty much the same as if you throw an ArithmeticException
, but an AssertionError
carries a completely different meaning (i.e. that there's a bug in your code, as opposed to someone recklessly tried to divide by zero). The same goes for toString()
. Be aware, however, that assertions are disabled by default, so the expression in the assert
statement will not be evaluated unless you enable them.
- There are three bugs in your
compares(Rational)
method: - In the second line, it should probably be
q.numerator
instead of onlynumerator
). - You confused the evaluations based on the signum: If
expression.signum() == -1
, thenthis
is less thanq
, and vice versa. - The algorithm is faulty: Comparing -2/1 to 3/-1 will produce incorrect results (it is possible that
numerator
is positive anddenominator
is negative) - In the method
toString()
: Returning"" + numerator
might save 6 characters of code, but, program-logic-wise, it is less direct that simply writingnumerator.toString()
and therefore more confusing to read. opposite()
is a rather meaningless method name, mathematically speaking. A more informative name might beadditiveInverse()
, or, analogous to theBigInteger
equivalent,negate()
.- There's a potential typo in
add()
: Could it be thatmcm
should actually belcm
for "least common multiple"?
A final comment on using BigInteger
. You list the fact that it has a built-in method for calculating the greatest common divisor as the only reason for using it. But a method for calculating the gcd is easily written in a few lines. If this is really your only reason for using BigInteger
, then you might consider writing a simple method for calculating the gcd of two int
s (or alternatively long
s, as ponomandr has suggested), and reap the benefits of using primitive values instead of BigInteger
s (e.g., as you have noticed, the use of operators). Of course, on the other hand, you would also have to deal with their limited range.
Update
- I found another bug: In
divide(Rational)
, you need to check forq.numerator
being zero, notq.denominator
.
- 2.2k
- 9
- 12