summaryrefslogtreecommitdiffstats
path: root/Lib/fractions.py
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2009-07-18 15:18:18 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2009-07-18 15:18:18 (GMT)
commit88a0a2e47fa60c26b3662ef5ee152fd78181c7b1 (patch)
treeb2b75d78afefe544fe4a52c95e01dec512ae5d64 /Lib/fractions.py
parent3bb474714ba859a008cd51545372eb2dfc63a349 (diff)
downloadcpython-88a0a2e47fa60c26b3662ef5ee152fd78181c7b1.zip
cpython-88a0a2e47fa60c26b3662ef5ee152fd78181c7b1.tar.gz
cpython-88a0a2e47fa60c26b3662ef5ee152fd78181c7b1.tar.bz2
Issue #6431: Fix Fraction comparisons with unknown types, and with
float infinities and nans. Backport of r74078 from py3k.
Diffstat (limited to 'Lib/fractions.py')
-rwxr-xr-xLib/fractions.py62
1 files changed, 32 insertions, 30 deletions
diff --git a/Lib/fractions.py b/Lib/fractions.py
index 15711ed..cc3ec11 100755
--- a/Lib/fractions.py
+++ b/Lib/fractions.py
@@ -486,54 +486,56 @@ class Fraction(Rational):
if isinstance(b, numbers.Complex) and b.imag == 0:
b = b.real
if isinstance(b, float):
- return a == a.from_float(b)
+ if math.isnan(b) or math.isinf(b):
+ # comparisons with an infinity or nan should behave in
+ # the same way for any finite a, so treat a as zero.
+ return 0.0 == b
+ else:
+ return a == a.from_float(b)
else:
- # XXX: If b.__eq__ is implemented like this method, it may
- # give the wrong answer after float(a) changes a's
- # value. Better ways of doing this are welcome.
- return float(a) == b
+ # Since a doesn't know how to compare with b, let's give b
+ # a chance to compare itself with a.
+ return NotImplemented
- def _subtractAndCompareToZero(a, b, op):
- """Helper function for comparison operators.
+ def _richcmp(self, other, op):
+ """Helper for comparison operators, for internal use only.
- Subtracts b from a, exactly if possible, and compares the
- result with 0 using op, in such a way that the comparison
- won't recurse. If the difference raises a TypeError, returns
- NotImplemented instead.
+ Implement comparison between a Rational instance `self`, and
+ either another Rational instance or a float `other`. If
+ `other` is not a Rational instance or a float, return
+ NotImplemented. `op` should be one of the six standard
+ comparison operators.
"""
- if isinstance(b, numbers.Complex) and b.imag == 0:
- b = b.real
- if isinstance(b, float):
- b = a.from_float(b)
- try:
- # XXX: If b <: Real but not <: Rational, this is likely
- # to fall back to a float. If the actual values differ by
- # less than MIN_FLOAT, this could falsely call them equal,
- # which would make <= inconsistent with ==. Better ways of
- # doing this are welcome.
- diff = a - b
- except TypeError:
+ # convert other to a Rational instance where reasonable.
+ if isinstance(other, Rational):
+ return op(self._numerator * other.denominator,
+ self._denominator * other.numerator)
+ if isinstance(other, numbers.Complex) and other.imag == 0:
+ other = other.real
+ if isinstance(other, float):
+ if math.isnan(other) or math.isinf(other):
+ return op(0.0, other)
+ else:
+ return op(self, self.from_float(other))
+ else:
return NotImplemented
- if isinstance(diff, Rational):
- return op(diff.numerator, 0)
- return op(diff, 0)
def __lt__(a, b):
"""a < b"""
- return a._subtractAndCompareToZero(b, operator.lt)
+ return a._richcmp(b, operator.lt)
def __gt__(a, b):
"""a > b"""
- return a._subtractAndCompareToZero(b, operator.gt)
+ return a._richcmp(b, operator.gt)
def __le__(a, b):
"""a <= b"""
- return a._subtractAndCompareToZero(b, operator.le)
+ return a._richcmp(b, operator.le)
def __ge__(a, b):
"""a >= b"""
- return a._subtractAndCompareToZero(b, operator.ge)
+ return a._richcmp(b, operator.ge)
def __nonzero__(a):
"""a != 0"""