X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=python%2Frational.py;h=f2d8360fb8004e4eb1b67ba8bec768dad08e2cd8;hb=06cda6485d929cd9f5d4fde22ab0840fd442cf73;hp=fc6fd169b5084390d1cdf7b9abc43dff0a0f36f6;hpb=188bbb97804465f6ce916ee9d38b5e7b1dfc6f5d;p=lilypond.git diff --git a/python/rational.py b/python/rational.py index fc6fd169b5..f2d8360fb8 100644 --- a/python/rational.py +++ b/python/rational.py @@ -2,7 +2,6 @@ from __future__ import division -import decimal as _decimal import math as _math def _gcf(a, b): @@ -39,18 +38,23 @@ class Rational(object): 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: @@ -95,8 +99,6 @@ class Rational(object): 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__ @@ -108,8 +110,6 @@ class Rational(object): 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): @@ -117,8 +117,6 @@ class Rational(object): 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): @@ -128,8 +126,6 @@ class Rational(object): 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__ @@ -140,8 +136,6 @@ class Rational(object): 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__ @@ -150,8 +144,6 @@ class Rational(object): 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__ @@ -184,9 +176,6 @@ class Rational(object): 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) @@ -198,65 +187,60 @@ class Rational(object): 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."""