from __future__ import division
-import decimal as _decimal
import math as _math
def _gcf(a, b):
raise TypeError('denominator must have integer type')
if not denominator:
raise ZeroDivisionError('rational construction')
- # Store the fraction in reduced form as _n/_d
- factor = _gcf(numerator, denominator)
- self._n = numerator // factor
- self._d = denominator // factor
+ self._d = denominator
+ self._n = numerator
+ self.normalize_self()
+ # Cancel the fraction to reduced form
+ def normalize_self(self):
+ factor = _gcf(self._n, self._d)
+ self._n = self._n // factor
+ self._d = self._d // factor
if self._d < 0:
self._n = -self._n
self._d = -self._d
+
def numerator(self):
- return self._n
+ return self._n
def denominator(self):
- return self._d
+ return self._d
def __repr__(self):
if self._d == 1:
return Rational(self._n + self._d * other, self._d)
elif isinstance(other, (float, complex)):
return float(self) + other
- elif isinstance(other, _decimal.Decimal):
- return self.decimal() + other
else:
return NotImplemented
__radd__ = __add__
return Rational(self._n - self._d * other, self._d)
elif isinstance(other, (float, complex)):
return float(self) - other
- elif isinstance(other, _decimal.Decimal):
- return self.decimal() - other
else:
return NotImplemented
def __rsub__(self, other):
return Rational(other * self._d - self._n, self._d)
elif isinstance(other, (float, complex)):
return other - float(self)
- elif isinstance(other, _decimal.Decimal):
- return other - self.decimal()
else:
return NotImplemented
def __mul__(self, other):
return Rational(self._n * other, self._d)
elif isinstance(other, (float, complex)):
return float(self) * other
- elif isinstance(other, _decimal.Decimal):
- return self.decimal() * other
else:
return NotImplemented
__rmul__ = __mul__
return Rational(self._n, self._d * other)
elif isinstance(other, (float, complex)):
return float(self) / other
- elif isinstance(other, _decimal.Decimal):
- return self.decimal() / other
else:
return NotImplemented
__div__ = __truediv__
return Rational(other * self._d, self._n)
elif isinstance(other, (float, complex)):
return other / float(self)
- elif isinstance(other, _decimal.Decimal):
- return other / self.decimal()
else:
return NotImplemented
__rdiv__ = __rtruediv__
return float(self) ** other
def __rpow__(self, other):
return other ** float(self)
- def decimal(self):
- """Return a Decimal approximation of self in the current context."""
- return _decimal.Decimal(self._n) / _decimal.Decimal(self._d)
def round(self, denominator):
"""Return self rounded to nearest multiple of 1/denominator."""
int_part, frac_part = divmod(self * denominator, 1)
else:
numerator = int_part + 1
return Rational(numerator, denominator)
- @staticmethod
- def from_exact_float(x):
- """Returns the exact Rational equivalent of x."""
- mantissa, exponent = _math.frexp(x)
- mantissa = int(mantissa * 2 ** 53)
- exponent -= 53
- if exponent < 0:
- return Rational(mantissa, 2 ** (-exponent))
- else:
- return Rational(mantissa * 2 ** exponent)
- @staticmethod
- def from_exact_decimal(x):
- """Returns the exact Rational equivalent of x."""
- sign, mantissa, exponent = x.as_tuple()
- sign = (1, -1)[sign]
- mantissa = sign * reduce(lambda a, b: 10 * a + b, mantissa)
- if exponent < 0:
- return Rational(mantissa, 10 ** (-exponent))
- else:
- return Rational(mantissa * 10 ** exponent)
- @staticmethod
- def approx_smallest_denominator(x, tolerance):
- """
- Returns a Rational approximation of x.
- Minimizes the denominator given a constraint on the error.
-
- x = the float or Decimal value to convert
- tolerance = maximum absolute error allowed,
- must be of the same type as x
- """
- tolerance = abs(tolerance)
- n = 1
- while True:
- m = int(round(x * n))
- result = Rational(m, n)
- if abs(result - x) < tolerance:
- return result
- n += 1
- @staticmethod
- def approx_smallest_error(x, maxDenominator):
- """
- Returns a Rational approximation of x.
- Minimizes the error given a constraint on the denominator.
-
- x = the float or Decimal value to convert
- maxDenominator = maximum denominator allowed
- """
- result = None
- minError = x
- for n in xrange(1, maxDenominator + 1):
- m = int(round(x * n))
- r = Rational(m, n)
- error = abs(r - x)
- if error == 0:
- return r
- elif error < minError:
- result = r
- minError = error
- return result
+
+
+
+def rational_from_exact_float(x):
+ """Returns the exact Rational equivalent of x."""
+ mantissa, exponent = _math.frexp(x)
+ mantissa = int(mantissa * 2 ** 53)
+ exponent -= 53
+ if exponent < 0:
+ return Rational(mantissa, 2 ** (-exponent))
+ else:
+ return Rational(mantissa * 2 ** exponent)
+
+
+
+def rational_approx_smallest_denominator(x, tolerance):
+ """
+ Returns a Rational approximation of x.
+ Minimizes the denominator given a constraint on the error.
+
+ x = the float or Decimal value to convert
+ tolerance = maximum absolute error allowed,
+ must be of the same type as x
+ """
+ tolerance = abs(tolerance)
+ n = 1
+ while True:
+ m = int(round(x * n))
+ result = Rational(m, n)
+ if abs(result - x) < tolerance:
+ return result
+ n += 1
+
+
+def rational_approx_smallest_error(x, maxDenominator):
+ """
+ Returns a Rational approximation of x.
+ Minimizes the error given a constraint on the denominator.
+
+ x = the float or Decimal value to convert
+ maxDenominator = maximum denominator allowed
+ """
+ result = None
+ minError = x
+ for n in xrange(1, maxDenominator + 1):
+ m = int(round(x * n))
+ r = Rational(m, n)
+ error = abs(r - x)
+ if error == 0:
+ return r
+ elif error < minError:
+ result = r
+ minError = error
+ return result
def divide(x, y):
"""Same as x/y, but returns a Rational if both are ints."""