Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
88a0a2e4
Commit
88a0a2e4
authored
Jul 18, 2009
by
Mark Dickinson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #6431: Fix Fraction comparisons with unknown types, and with
float infinities and nans. Backport of r74078 from py3k.
parent
3bb47471
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
162 additions
and
30 deletions
+162
-30
Lib/fractions.py
Lib/fractions.py
+32
-30
Lib/test/test_fractions.py
Lib/test/test_fractions.py
+126
-0
Misc/NEWS
Misc/NEWS
+4
-0
No files found.
Lib/fractions.py
View file @
88a0a2e4
...
...
@@ -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 f
unction for comparison operators
.
def
_
richcmp
(
self
,
other
,
op
):
"""Helper f
or 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
e
xcept
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
))
e
lse
:
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"""
...
...
Lib/test/test_fractions.py
View file @
88a0a2e4
...
...
@@ -3,6 +3,7 @@
from
decimal
import
Decimal
from
test.test_support
import
run_unittest
import
math
import
numbers
import
operator
import
fractions
import
unittest
...
...
@@ -11,6 +12,69 @@ from cPickle import dumps, loads
F
=
fractions
.
Fraction
gcd
=
fractions
.
gcd
class
DummyFloat
(
object
):
"""Dummy float class for testing comparisons with Fractions"""
def
__init__
(
self
,
value
):
if
not
isinstance
(
value
,
float
):
raise
TypeError
(
"DummyFloat can only be initialized from float"
)
self
.
value
=
value
def
_richcmp
(
self
,
other
,
op
):
if
isinstance
(
other
,
numbers
.
Rational
):
return
op
(
F
.
from_float
(
self
.
value
),
other
)
elif
isinstance
(
other
,
DummyFloat
):
return
op
(
self
.
value
,
other
.
value
)
else
:
return
NotImplemented
def
__eq__
(
self
,
other
):
return
self
.
_richcmp
(
other
,
operator
.
eq
)
def
__le__
(
self
,
other
):
return
self
.
_richcmp
(
other
,
operator
.
le
)
def
__lt__
(
self
,
other
):
return
self
.
_richcmp
(
other
,
operator
.
lt
)
def
__ge__
(
self
,
other
):
return
self
.
_richcmp
(
other
,
operator
.
ge
)
def
__gt__
(
self
,
other
):
return
self
.
_richcmp
(
other
,
operator
.
gt
)
# shouldn't be calling __float__ at all when doing comparisons
def
__float__
(
self
):
assert
False
,
"__float__ should not be invoked for comparisons"
# same goes for subtraction
def
__sub__
(
self
,
other
):
assert
False
,
"__sub__ should not be invoked for comparisons"
__rsub__
=
__sub__
class
DummyRational
(
object
):
"""Test comparison of Fraction with a naive rational implementation."""
def
__init__
(
self
,
num
,
den
):
g
=
gcd
(
num
,
den
)
self
.
num
=
num
//
g
self
.
den
=
den
//
g
def
__eq__
(
self
,
other
):
if
isinstance
(
other
,
fractions
.
Fraction
):
return
(
self
.
num
==
other
.
_numerator
and
self
.
den
==
other
.
_denominator
)
else
:
return
NotImplemented
def
__lt__
(
self
,
other
):
return
(
self
.
num
*
other
.
_denominator
<
self
.
den
*
other
.
_numerator
)
def
__gt__
(
self
,
other
):
return
(
self
.
num
*
other
.
_denominator
>
self
.
den
*
other
.
_numerator
)
def
__le__
(
self
,
other
):
return
(
self
.
num
*
other
.
_denominator
<=
self
.
den
*
other
.
_numerator
)
def
__ge__
(
self
,
other
):
return
(
self
.
num
*
other
.
_denominator
>=
self
.
den
*
other
.
_numerator
)
# this class is for testing comparisons; conversion to float
# should never be used for a comparison, since it loses accuracy
def
__float__
(
self
):
assert
False
,
"__float__ should not be invoked"
class
GcdTest
(
unittest
.
TestCase
):
...
...
@@ -311,6 +375,50 @@ class FractionTest(unittest.TestCase):
self
.
assertFalse
(
F
(
1
,
2
)
!=
F
(
1
,
2
))
self
.
assertTrue
(
F
(
1
,
2
)
!=
F
(
1
,
3
))
def
testComparisonsDummyRational
(
self
):
self
.
assertTrue
(
F
(
1
,
2
)
==
DummyRational
(
1
,
2
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
==
F
(
1
,
2
))
self
.
assertFalse
(
F
(
1
,
2
)
==
DummyRational
(
3
,
4
))
self
.
assertFalse
(
DummyRational
(
3
,
4
)
==
F
(
1
,
2
))
self
.
assertTrue
(
F
(
1
,
2
)
<
DummyRational
(
3
,
4
))
self
.
assertFalse
(
F
(
1
,
2
)
<
DummyRational
(
1
,
2
))
self
.
assertFalse
(
F
(
1
,
2
)
<
DummyRational
(
1
,
7
))
self
.
assertFalse
(
F
(
1
,
2
)
>
DummyRational
(
3
,
4
))
self
.
assertFalse
(
F
(
1
,
2
)
>
DummyRational
(
1
,
2
))
self
.
assertTrue
(
F
(
1
,
2
)
>
DummyRational
(
1
,
7
))
self
.
assertTrue
(
F
(
1
,
2
)
<=
DummyRational
(
3
,
4
))
self
.
assertTrue
(
F
(
1
,
2
)
<=
DummyRational
(
1
,
2
))
self
.
assertFalse
(
F
(
1
,
2
)
<=
DummyRational
(
1
,
7
))
self
.
assertFalse
(
F
(
1
,
2
)
>=
DummyRational
(
3
,
4
))
self
.
assertTrue
(
F
(
1
,
2
)
>=
DummyRational
(
1
,
2
))
self
.
assertTrue
(
F
(
1
,
2
)
>=
DummyRational
(
1
,
7
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
<
F
(
3
,
4
))
self
.
assertFalse
(
DummyRational
(
1
,
2
)
<
F
(
1
,
2
))
self
.
assertFalse
(
DummyRational
(
1
,
2
)
<
F
(
1
,
7
))
self
.
assertFalse
(
DummyRational
(
1
,
2
)
>
F
(
3
,
4
))
self
.
assertFalse
(
DummyRational
(
1
,
2
)
>
F
(
1
,
2
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
>
F
(
1
,
7
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
<=
F
(
3
,
4
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
<=
F
(
1
,
2
))
self
.
assertFalse
(
DummyRational
(
1
,
2
)
<=
F
(
1
,
7
))
self
.
assertFalse
(
DummyRational
(
1
,
2
)
>=
F
(
3
,
4
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
>=
F
(
1
,
2
))
self
.
assertTrue
(
DummyRational
(
1
,
2
)
>=
F
(
1
,
7
))
def
testComparisonsDummyFloat
(
self
):
x
=
DummyFloat
(
1.
/
3.
)
y
=
F
(
1
,
3
)
self
.
assertTrue
(
x
!=
y
)
self
.
assertTrue
(
x
<
y
or
x
>
y
)
self
.
assertFalse
(
x
==
y
)
self
.
assertFalse
(
x
<=
y
and
x
>=
y
)
self
.
assertTrue
(
y
!=
x
)
self
.
assertTrue
(
y
<
x
or
y
>
x
)
self
.
assertFalse
(
y
==
x
)
self
.
assertFalse
(
y
<=
x
and
y
>=
x
)
def
testMixedLess
(
self
):
self
.
assertTrue
(
2
<
F
(
5
,
2
))
self
.
assertFalse
(
2
<
F
(
4
,
2
))
...
...
@@ -322,6 +430,13 @@ class FractionTest(unittest.TestCase):
self
.
assertTrue
(
0.4
<
F
(
1
,
2
))
self
.
assertFalse
(
0.5
<
F
(
1
,
2
))
self
.
assertFalse
(
float
(
'inf'
)
<
F
(
1
,
2
))
self
.
assertTrue
(
float
(
'-inf'
)
<
F
(
0
,
10
))
self
.
assertFalse
(
float
(
'nan'
)
<
F
(
-
3
,
7
))
self
.
assertTrue
(
F
(
1
,
2
)
<
float
(
'inf'
))
self
.
assertFalse
(
F
(
17
,
12
)
<
float
(
'-inf'
))
self
.
assertFalse
(
F
(
144
,
-
89
)
<
float
(
'nan'
))
def
testMixedLessEqual
(
self
):
self
.
assertTrue
(
0.5
<=
F
(
1
,
2
))
self
.
assertFalse
(
0.6
<=
F
(
1
,
2
))
...
...
@@ -332,6 +447,13 @@ class FractionTest(unittest.TestCase):
self
.
assertTrue
(
F
(
4
,
2
)
<=
2
)
self
.
assertFalse
(
F
(
5
,
2
)
<=
2
)
self
.
assertFalse
(
float
(
'inf'
)
<=
F
(
1
,
2
))
self
.
assertTrue
(
float
(
'-inf'
)
<=
F
(
0
,
10
))
self
.
assertFalse
(
float
(
'nan'
)
<=
F
(
-
3
,
7
))
self
.
assertTrue
(
F
(
1
,
2
)
<=
float
(
'inf'
))
self
.
assertFalse
(
F
(
17
,
12
)
<=
float
(
'-inf'
))
self
.
assertFalse
(
F
(
144
,
-
89
)
<=
float
(
'nan'
))
def
testBigFloatComparisons
(
self
):
# Because 10**23 can't be represented exactly as a float:
self
.
assertFalse
(
F
(
10
**
23
)
==
float
(
10
**
23
))
...
...
@@ -356,6 +478,10 @@ class FractionTest(unittest.TestCase):
self
.
assertFalse
(
2
==
F
(
3
,
2
))
self
.
assertTrue
(
F
(
4
,
2
)
==
2
)
self
.
assertFalse
(
F
(
5
,
2
)
==
2
)
self
.
assertFalse
(
F
(
5
,
2
)
==
float
(
'nan'
))
self
.
assertFalse
(
float
(
'nan'
)
==
F
(
3
,
7
))
self
.
assertFalse
(
F
(
5
,
2
)
==
float
(
'inf'
))
self
.
assertFalse
(
float
(
'-inf'
)
==
F
(
2
,
5
))
def
testStringification
(
self
):
self
.
assertEquals
(
"Fraction(7, 3)"
,
repr
(
F
(
7
,
3
)))
...
...
Misc/NEWS
View file @
88a0a2e4
...
...
@@ -352,6 +352,10 @@ Core and Builtins
Library
-------
- Issue #6431: Make Fraction type return NotImplemented when it doesn't
know how to handle a comparison without loss of precision. Also add
correct handling of infinities and nans for comparisons with float.
- Issue #6415: Fixed warnings.warn sagfault on bad formatted string.
- Issue #6466: now distutils.cygwinccompiler and distutils.emxccompiler
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment