1 """Implementation of rational arithmetic."""
3 from __future__ import division
8 """Returns the greatest common factor of a and b."""
15 class Rational(object):
17 This class provides an exact representation of rational numbers.
19 All of the standard arithmetic operators are provided. In mixed-type
20 expressions, an int or a long can be converted to a Rational without
21 loss of precision, and will be done as such.
23 Rationals can be implicity (using binary operators) or explicity
24 (using float(x) or x.decimal()) converted to floats or Decimals;
25 this may cause a loss of precision. The reverse conversions can be
26 done without loss of precision, and are performed with the
27 from_exact_float and from_exact_decimal static methods. However,
28 because of rounding error in the original values, this tends to
29 produce "ugly" fractions. "Nicer" conversions to Rational can be made
30 with approx_smallest_denominator or approx_smallest_error.
33 def __init__(self, numerator, denominator=1):
34 """Contructs the Rational object for numerator/denominator."""
35 if not isinstance(numerator, (int, long)):
36 raise TypeError('numerator must have integer type')
37 if not isinstance(denominator, (int, long)):
38 raise TypeError('denominator must have integer type')
40 raise ZeroDivisionError('rational construction')
44 # Cancel the fraction to reduced form
45 def normalize_self(self):
46 factor = _gcf(self._n, self._d)
47 self._n = self._n // factor
48 self._d = self._d // factor
56 def denominator(self):
61 return "Rational(%d)" % self._n
63 return "Rational(%d, %d)" % (self._n, self._d)
68 return "%d/%d" % (self._n, self._d)
71 return hash(float(self))
73 return hash(long(self))
75 return self._n / self._d
78 return -int(-self._n // self._d)
80 return int(self._n // self._d)
82 return long(int(self))
83 def __nonzero__(self):
88 return Rational(-self._n, self._d)
94 def __add__(self, other):
95 if isinstance(other, Rational):
96 return Rational(self._n * other._d + self._d * other._n,
98 elif isinstance(other, (int, long)):
99 return Rational(self._n + self._d * other, self._d)
100 elif isinstance(other, (float, complex)):
101 return float(self) + other
103 return NotImplemented
105 def __sub__(self, other):
106 if isinstance(other, Rational):
107 return Rational(self._n * other._d - self._d * other._n,
109 elif isinstance(other, (int, long)):
110 return Rational(self._n - self._d * other, self._d)
111 elif isinstance(other, (float, complex)):
112 return float(self) - other
114 return NotImplemented
115 def __rsub__(self, other):
116 if isinstance(other, (int, long)):
117 return Rational(other * self._d - self._n, self._d)
118 elif isinstance(other, (float, complex)):
119 return other - float(self)
121 return NotImplemented
122 def __mul__(self, other):
123 if isinstance(other, Rational):
124 return Rational(self._n * other._n, self._d * other._d)
125 elif isinstance(other, (int, long)):
126 return Rational(self._n * other, self._d)
127 elif isinstance(other, (float, complex)):
128 return float(self) * other
130 return NotImplemented
132 def __truediv__(self, other):
133 if isinstance(other, Rational):
134 return Rational(self._n * other._d, self._d * other._n)
135 elif isinstance(other, (int, long)):
136 return Rational(self._n, self._d * other)
137 elif isinstance(other, (float, complex)):
138 return float(self) / other
140 return NotImplemented
141 __div__ = __truediv__
142 def __rtruediv__(self, other):
143 if isinstance(other, (int, long)):
144 return Rational(other * self._d, self._n)
145 elif isinstance(other, (float, complex)):
146 return other / float(self)
148 return NotImplemented
149 __rdiv__ = __rtruediv__
150 def __floordiv__(self, other):
151 truediv = self / other
152 if isinstance(truediv, Rational):
153 return truediv._n // truediv._d
156 def __rfloordiv__(self, other):
157 return (other / self) // 1
158 def __mod__(self, other):
159 return self - self // other * other
160 def __rmod__(self, other):
161 return other - other // self * self
162 def __divmod__(self, other):
163 return self // other, self % other
164 def __cmp__(self, other):
166 return cmp(self._n, 0)
168 return cmp(self - other, 0)
169 def __pow__(self, other):
170 if isinstance(other, (int, long)):
172 return Rational(self._d ** -other, self._n ** -other)
174 return Rational(self._n ** other, self._d ** other)
176 return float(self) ** other
177 def __rpow__(self, other):
178 return other ** float(self)
179 def round(self, denominator):
180 """Return self rounded to nearest multiple of 1/denominator."""
181 int_part, frac_part = divmod(self * denominator, 1)
182 round_direction = cmp(frac_part * 2, 1)
183 if round_direction == 0:
184 numerator = int_part + (int_part & 1) # round to even
185 elif round_direction < 0:
188 numerator = int_part + 1
189 return Rational(numerator, denominator)
193 def rational_from_exact_float(x):
194 """Returns the exact Rational equivalent of x."""
195 mantissa, exponent = _math.frexp(x)
196 mantissa = int(mantissa * 2 ** 53)
199 return Rational(mantissa, 2 ** (-exponent))
201 return Rational(mantissa * 2 ** exponent)
205 def rational_approx_smallest_denominator(x, tolerance):
207 Returns a Rational approximation of x.
208 Minimizes the denominator given a constraint on the error.
210 x = the float or Decimal value to convert
211 tolerance = maximum absolute error allowed,
212 must be of the same type as x
214 tolerance = abs(tolerance)
217 m = int(round(x * n))
218 result = Rational(m, n)
219 if abs(result - x) < tolerance:
224 def rational_approx_smallest_error(x, maxDenominator):
226 Returns a Rational approximation of x.
227 Minimizes the error given a constraint on the denominator.
229 x = the float or Decimal value to convert
230 maxDenominator = maximum denominator allowed
234 for n in xrange(1, maxDenominator + 1):
235 m = int(round(x * n))
240 elif error < minError:
246 """Same as x/y, but returns a Rational if both are ints."""
247 if isinstance(x, (int, long)) and isinstance(y, (int, long)):
248 return Rational(x, y)