#ifdef WIN32 #include #else #include #endif #include "fraction.h" #include "safeops.h" #include "gcd.h" int LeastCommonMultiple(int i, int j) { if(i == 0 && j == 0) { return 0; } bool ok; int retVal = safeMul(i, (j/GreatestCommonDivisor(i,j)), ok); if(!ok) { return -1; } else { return retVal; } } const Fraction NAN; const Fraction INF(1, 0); const Fraction NEGINF(-1, 0); // initialize the fraction to NaN Fraction::Fraction() : num(0), den(0) { } // initialize the fraction to i Fraction::Fraction(int i) : num(i), den(1) { } // initialize the fraction to numerator/denominator Fraction::Fraction(int numer, int denom) : num(numer), den(denom) { if(den < 0) { // should not have negative denominators num = -num; den = -den; } if(num == 0 && den == 0) { return; } // reduce to lowest terms int gcd = GreatestCommonDivisor(num, den); num /= gcd; den /= gcd; } bool Fraction::isNeg() { return(num < 0); } Fraction Fraction::operator+(Fraction rhs) { // handle weird cases if(NaN()) { // nothing will make this a number again return NAN; } else if(Inf()) { // only NaN or -Inf cause non-Inf values if(rhs.NaN() || rhs.NegInf()) { return NAN; } else { return INF; } } else if(NegInf()) { // only NaN or Inf cause non-NegInf values if(rhs.NaN() || rhs.Inf()) { return NAN; } else { return NEGINF; } } else if (rhs.NaN() || rhs.Inf() || rhs.NegInf()) { // first arg is normal return rhs; } // if you got this far, both arguments are normal bool ok; int lcm = LeastCommonMultiple(den, rhs.den); if(lcm < 0) { // least common multiple caused overflow return NAN; } int a = safeMul(num,(lcm/den), ok); if(!ok) { // overflow return NAN; } int b = safeMul(rhs.num,(lcm/rhs.den), ok); if(!ok) { // overflow return NAN; } int top = safeAdd(a, b, ok); if(!ok) { // overflow return NAN; } Fraction retVal(top, lcm); return retVal; } Fraction Fraction::operator-(Fraction rhs) { return operator+(-rhs); } Fraction Fraction::operator*(Fraction rhs) { // handle weird cases if(NaN()) { // nothing will make this a number again return NAN; } else if(Inf()) { // check sign and NaN-ness if(rhs.NaN()) { return NAN; } else if(rhs.isNeg()) { return NEGINF; } else if(rhs == 0) { // this calls the constructor that takes one int return NAN; } else { return INF; } } else if(NegInf()) { // check sign and NaN-ness if(rhs.NaN()) { return NAN; } else if(rhs.isNeg()) { return INF; } else if(rhs == 0) { // this calls the constructor that takes one int return NAN; } else { return NEGINF; } } else if (rhs.NaN() || rhs.Inf() || rhs.NegInf()) { // first arg is normal if(isNeg()) { return -rhs; } else if(operator==(0)) { return NAN; } else { return rhs; } } // if you got this far, both arguments are normal bool ok; int gcd1 = GreatestCommonDivisor(num, rhs.den); int gcd2 = GreatestCommonDivisor(rhs.num, den); int top = safeMul(num/gcd1, rhs.num/gcd2, ok); if(!ok) { // overflow return NAN; } int bot = safeMul(den/gcd2, rhs.den/gcd1, ok); if(!ok) { // overflow return NAN; } Fraction retVal(top, bot); return retVal; } Fraction Fraction::operator/(Fraction rhs) { Fraction f(rhs.den, rhs.num); return operator*(f); } // unary - operator Fraction Fraction::operator-() { Fraction retVal(-num, den); return retVal; } bool Fraction::operator==(Fraction rhs) { return(num == rhs.num && den == rhs.den); } // return true iff the fraction is Not a Number bool Fraction::NaN() { return(num == 0 && den == 0); } // return true iff the fraction is positive infinity bool Fraction::Inf() { return(num == 1 && den == 0); } // return true iff the fraction is negative infinity bool Fraction::NegInf() { return(num == -1 && den == 0); } // print the fraction as / to the stream os void Fraction::print(ostream& os) { if(NaN()) { os << "NaN"; } else if(Inf()) { os << "Inf"; } else if(NegInf()) { os << "-Inf"; } else if(den == 1) { // print as an int os << num; } else { // print as a fraction os << num << '/' << den; } } // fill the buffer buf with the decimal value of the fraction to as // high a precision as possible without exceeding length len-1 // return false if len did not allow sufficient room to represent // NaN, Inf, or -Inf, or the number prior to the decimal point, or if // long division causes overflow bool Fraction::expand(char buf[], int len) { ostrstream ost(buf, len); if(NaN()) { ost << "NaN" << ends; return !ost.fail(); } if(Inf()) { ost << "Inf" << ends; return !ost.fail(); } if(NegInf()) { ost << "-Inf" << ends; return !ost.fail(); } // if you get this far, its a normal number int q, r; if(num < 0) { // handle negative numbers ost << "-"; q = -num/den; r = (-num)%den; } else { q = num/den; r = num%den; } ost << q; if(ost.fail()) { return false; } if(r) { // there is a remainder bool ok; int t; ost << '.'; while(r && ost) { // still a remainder, and we have not run out of buffer t = safeMul(r, 10, ok); if(!ok) { // overflow cerr << "overflow in long division" << endl; return false; } q = t/den; r = t%den; ost << q; } } ost << ends; if(ost.fail()) { // we overflowed, go back and null terminate buf[len-1] = '\0'; } return true; } // return the double best approximating the value of the fraction double Fraction::toDouble() { return(double(num)/den); } ostream& operator<<(ostream& os, Fraction f) { f.print(os); return os; }