Question

I would like to check for equality 2 fractions. I think that the method I wrote is not correct because of possible loss of accuracy. Am I right or wrong?

public boolean equals(Rational r) {
    return (double) numerator / denominator == (double) r.numerator / r.denominator;
}
Was it helpful?

Solution

I think this would be better:

public boolean equals(Rational r) {
  return numerator * r.denominator == r.numerator * denominator;
}

OTHER TIPS

You are correct, this check will fail in a lot of cases.

The only way to make it work is to either convert the result to a BigDecimal or a String and then compare that or to define an allowed error - i.e.:

public boolean equals(Rational r) {
    double c1 = ((double) numerator) / denominator;
    double c2 = ((double) r.numerator) / r.denominator;
    return c2 > (c1-allowedError) && c2 < (c1+allowedError);
}

You can do this way

public boolean equals(Rational r) {
    return numerator * r.denominator == r.numerator * denominator;
}

If your rationals have reduced all common factors out of numerator and denominator, then you can do this:

return (numerator == r.numerator) && (denominator == r.denominator);

If your rationals have not done that factorization, then I suggest you implement a GCD function that will allow you to do that. Then compare the reduced numerator and denominator against the reduced r.numerator and r.denominator.

Example, assuming int is the underlying type for your rational numerator and denominator:

int gcd_lhs = gcd( numerator,   denominator   );
int gcd_rhs = gcd( r.numerator, r.denominator );

int lhs_red_num = numerator   / gcd_lhs;
int lhs_red_den = denominator / gcd_lhs;
int rhs_red_num = r.numerator   / gcd_rhs;
int rhs_red_den = r.denominator / gcd_rhs;

return (lhs_red_num == rhs_red_num) && (lhs_red_den == rhs_red_den);

This approach has the advantage of being exact in all cases, and it will never exceed the precision of the underlying type that you're using for numerator and denominator.

Approaches relying on multiplication, or on dividing something out of the values other than the GCD risk overflowing the precision available in the underlying types. (Division can "overflow" the available precision by producing a repeating binary fraction that must be rounded to fit in the available bits, assuming you cast to double first. Dividing by GCD is always exact, and remain as integer division.)

You can mitigate that by using a BigInteger type and applying the cross-multiplication approach. That has its own costs. If you're already using BigInteger to store numerator and denominator, that may be the easiest approach, though.

One way to do this is as follows:

boolean eq = Math.abs( (double) numerator / denominator - 
                       (double) r.numerator / r.denominator ) < EPS;

where

EPS is some small positive constant like e.g. EPS=0.00001.

If the eq variable is set to true, it means your numbers are "equal"
except for possible precision errors which you want to ignore here.

Of course in Java operations with floating point
numbers are not 100% precise as in many other languages.

Alternatively, as other people suggested here,
you may decide to not use doubles at all for this
particular task / but you cannot always avoid them ;) /

Using == with doubles can be dangerous because certain fraction (including some that are expressible in decimal) can't be expressed in a binary floating point system

You can rearrange your equations such that you never need doubles (assumimg your numerator and denominator are integers)

r.denominator*numerator==denominator*r.numerator
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top