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
7c63eee4
Commit
7c63eee4
authored
Apr 02, 2010
by
Mark Dickinson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #8294: Allow float and Decimal arguments in Fraction constructor.
parent
58c1e788
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
121 additions
and
20 deletions
+121
-20
Doc/library/fractions.rst
Doc/library/fractions.rst
+38
-11
Lib/fractions.py
Lib/fractions.py
+54
-8
Lib/test/test_fractions.py
Lib/test/test_fractions.py
+26
-1
Misc/NEWS
Misc/NEWS
+3
-0
No files found.
Doc/library/fractions.rst
View file @
7c63eee4
...
@@ -17,17 +17,24 @@ another rational number, or from a string.
...
@@ -17,17 +17,24 @@ another rational number, or from a string.
.. class:: Fraction(numerator=0, denominator=1)
.. class:: Fraction(numerator=0, denominator=1)
Fraction(other_fraction)
Fraction(other_fraction)
Fraction(float)
Fraction(decimal)
Fraction(string)
Fraction(string)
The first version requires that *numerator* and *denominator* are
The first version requires that *numerator* and *denominator* are instances
instances of :class:`numbers.Rational` and returns a new
of :class:`numbers.Rational` and returns a new :class:`Fraction` instance
:class:`Fraction` instance with value ``numerator/denominator``. If
with value ``numerator/denominator``. If *denominator* is :const:`0`, it
*denominator* is :const:`0`, it raises a
raises a :exc:`ZeroDivisionError`. The second version requires that
:exc:`ZeroDivisionError`. The second version requires that
*other_fraction* is an instance of :class:`numbers.Rational` and returns a
*other_fraction* is an instance of :class:`numbers.Rational` and
:class:`Fraction` instance with the same value. The next two versions accept
returns an :class:`Fraction` instance with the same value. The
either a :class:`float` or a :class:`decimal.Decimal` instance, and return a
last version of the constructor expects a string or unicode
:class:`Fraction` instance with exactly the same value. Note that due to the
instance. The usual form for this instance is::
usual issues with binary floating-point (see :ref:`tut-fp-issues`), the
argument to ``Fraction(1.1)`` is not exactly equal to 11/10, and so
``Fraction(1.1)`` does *not* return ``Fraction(11, 10)`` as one might expect.
(But see the documentation for the :meth:`limit_denominator` method below.)
The last version of the constructor expects a string or unicode instance.
The usual form for this instance is::
[sign] numerator ['/' denominator]
[sign] numerator ['/' denominator]
...
@@ -57,6 +64,13 @@ another rational number, or from a string.
...
@@ -57,6 +64,13 @@ another rational number, or from a string.
Fraction(-1, 8)
Fraction(-1, 8)
>>> Fraction('7e-6')
>>> Fraction('7e-6')
Fraction(7, 1000000)
Fraction(7, 1000000)
>>> Fraction(2.25)
Fraction(9, 4)
>>> Fraction(1.1)
Fraction(2476979795053773, 2251799813685248)
>>> from decimal import Decimal
>>> Fraction(Decimal('1.1'))
Fraction(11, 10)
The :class:`Fraction` class inherits from the abstract base class
The :class:`Fraction` class inherits from the abstract base class
...
@@ -65,6 +79,10 @@ another rational number, or from a string.
...
@@ -65,6 +79,10 @@ another rational number, or from a string.
and should be treated as immutable. In addition,
and should be treated as immutable. In addition,
:class:`Fraction` has the following methods:
:class:`Fraction` has the following methods:
.. versionchanged:: 2.7
The :class:`Fraction` constructor now accepts :class:`float` and
:class:`decimal.Decimal` instances.
.. method:: from_float(flt)
.. method:: from_float(flt)
...
@@ -72,12 +90,19 @@ another rational number, or from a string.
...
@@ -72,12 +90,19 @@ another rational number, or from a string.
value of *flt*, which must be a :class:`float`. Beware that
value of *flt*, which must be a :class:`float`. Beware that
``Fraction.from_float(0.3)`` is not the same value as ``Fraction(3, 10)``
``Fraction.from_float(0.3)`` is not the same value as ``Fraction(3, 10)``
.. note:: From Python 2.7 onwards, you can also construct a
:class:`Fraction` instance directly from a :class:`float`.
.. method:: from_decimal(dec)
.. method:: from_decimal(dec)
This class method constructs a :class:`Fraction` representing the exact
This class method constructs a :class:`Fraction` representing the exact
value of *dec*, which must be a :class:`decimal.Decimal`.
value of *dec*, which must be a :class:`decimal.Decimal`.
.. note:: From Python 2.7 onwards, you can also construct a
:class:`Fraction` instance directly from a :class:`decimal.Decimal`
instance.
.. method:: limit_denominator(max_denominator=1000000)
.. method:: limit_denominator(max_denominator=1000000)
...
@@ -92,10 +117,12 @@ another rational number, or from a string.
...
@@ -92,10 +117,12 @@ another rational number, or from a string.
or for recovering a rational number that's represented as a float:
or for recovering a rational number that's represented as a float:
>>> from math import pi, cos
>>> from math import pi, cos
>>> Fraction
.from_float
(cos(pi/3))
>>> Fraction(cos(pi/3))
Fraction(4503599627370497, 9007199254740992)
Fraction(4503599627370497, 9007199254740992)
>>> Fraction
.from_float
(cos(pi/3)).limit_denominator()
>>> Fraction(cos(pi/3)).limit_denominator()
Fraction(1, 2)
Fraction(1, 2)
>>> Fraction(1.1).limit_denominator()
Fraction(11, 10)
.. function:: gcd(a, b)
.. function:: gcd(a, b)
...
...
Lib/fractions.py
View file @
7c63eee4
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
"""Rational, infinite-precision, real numbers."""
"""Rational, infinite-precision, real numbers."""
from
__future__
import
division
from
__future__
import
division
from
decimal
import
Decimal
import
math
import
math
import
numbers
import
numbers
import
operator
import
operator
...
@@ -43,13 +44,21 @@ _RATIONAL_FORMAT = re.compile(r"""
...
@@ -43,13 +44,21 @@ _RATIONAL_FORMAT = re.compile(r"""
class
Fraction
(
Rational
):
class
Fraction
(
Rational
):
"""This class implements rational numbers.
"""This class implements rational numbers.
Fraction(8, 6) will produce a rational number equivalent to
In the two-argument form of the constructor, Fraction(8, 6) will
4/3. Both arguments must be Integral. The numerator defaults to 0
produce a rational number equivalent to 4/3. Both arguments must
and the denominator defaults to 1 so that Fraction(3) == 3 and
be Rational. The numerator defaults to 0 and the denominator
Fraction() == 0.
defaults to 1 so that Fraction(3) == 3 and
Fraction() == 0.
Fractions can also be constructed from strings of the form
Fractions can also be constructed from:
'[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
- numeric strings similar to those accepted by the
float constructor (for example, '-2.3' or '1e10')
- strings of the form '123/456'
- float and Decimal instances
- other Rational instances (including integers)
"""
"""
...
@@ -59,8 +68,32 @@ class Fraction(Rational):
...
@@ -59,8 +68,32 @@ class Fraction(Rational):
def
__new__
(
cls
,
numerator
=
0
,
denominator
=
None
):
def
__new__
(
cls
,
numerator
=
0
,
denominator
=
None
):
"""Constructs a Fraction.
"""Constructs a Fraction.
Takes a string like '3/2' or '1.5', another Fraction, or a
Takes a string like '3/2' or '1.5', another Rational instance, a
numerator/denominator pair.
numerator/denominator pair, or a float.
Examples
--------
>>> Fraction(10, -8)
Fraction(-5, 4)
>>> Fraction(Fraction(1, 7), 5)
Fraction(1, 35)
>>> Fraction(Fraction(1, 7), Fraction(2, 3))
Fraction(3, 14)
>>> Fraction('314')
Fraction(314, 1)
>>> Fraction('-35/4')
Fraction(-35, 4)
>>> Fraction('3.1415') # conversion from numeric string
Fraction(6283, 2000)
>>> Fraction('-47e-2') # string may include a decimal exponent
Fraction(-47, 100)
>>> Fraction(1.47) # direct construction from float (exact conversion)
Fraction(6620291452234629, 4503599627370496)
>>> Fraction(2.25)
Fraction(9, 4)
>>> Fraction(Decimal('1.47'))
Fraction(147, 100)
"""
"""
self
=
super
(
Fraction
,
cls
).
__new__
(
cls
)
self
=
super
(
Fraction
,
cls
).
__new__
(
cls
)
...
@@ -71,6 +104,19 @@ class Fraction(Rational):
...
@@ -71,6 +104,19 @@ class Fraction(Rational):
self
.
_denominator
=
numerator
.
denominator
self
.
_denominator
=
numerator
.
denominator
return
self
return
self
elif
isinstance
(
numerator
,
float
):
# Exact conversion from float
value
=
Fraction
.
from_float
(
numerator
)
self
.
_numerator
=
value
.
_numerator
self
.
_denominator
=
value
.
_denominator
return
self
elif
isinstance
(
numerator
,
Decimal
):
value
=
Fraction
.
from_decimal
(
numerator
)
self
.
_numerator
=
value
.
_numerator
self
.
_denominator
=
value
.
_denominator
return
self
elif
isinstance
(
numerator
,
basestring
):
elif
isinstance
(
numerator
,
basestring
):
# Handle construction from strings.
# Handle construction from strings.
m
=
_RATIONAL_FORMAT
.
match
(
numerator
)
m
=
_RATIONAL_FORMAT
.
match
(
numerator
)
...
...
Lib/test/test_fractions.py
View file @
7c63eee4
...
@@ -12,6 +12,11 @@ from cPickle import dumps, loads
...
@@ -12,6 +12,11 @@ from cPickle import dumps, loads
F
=
fractions
.
Fraction
F
=
fractions
.
Fraction
gcd
=
fractions
.
gcd
gcd
=
fractions
.
gcd
# decorator for skipping tests on non-IEEE 754 platforms
requires_IEEE_754
=
unittest
.
skipUnless
(
float
.
__getformat__
(
"double"
).
startswith
(
"IEEE"
),
"test requires IEEE 754 doubles"
)
class
DummyFloat
(
object
):
class
DummyFloat
(
object
):
"""Dummy float class for testing comparisons with Fractions"""
"""Dummy float class for testing comparisons with Fractions"""
...
@@ -137,13 +142,33 @@ class FractionTest(unittest.TestCase):
...
@@ -137,13 +142,33 @@ class FractionTest(unittest.TestCase):
self
.
assertRaisesMessage
(
ZeroDivisionError
,
"Fraction(12, 0)"
,
self
.
assertRaisesMessage
(
ZeroDivisionError
,
"Fraction(12, 0)"
,
F
,
12
,
0
)
F
,
12
,
0
)
self
.
assertRaises
(
TypeError
,
F
,
1.5
)
self
.
assertRaises
(
TypeError
,
F
,
1.5
+
3j
)
self
.
assertRaises
(
TypeError
,
F
,
1.5
+
3j
)
self
.
assertRaises
(
TypeError
,
F
,
"3/2"
,
3
)
self
.
assertRaises
(
TypeError
,
F
,
"3/2"
,
3
)
self
.
assertRaises
(
TypeError
,
F
,
3
,
0j
)
self
.
assertRaises
(
TypeError
,
F
,
3
,
0j
)
self
.
assertRaises
(
TypeError
,
F
,
3
,
1j
)
self
.
assertRaises
(
TypeError
,
F
,
3
,
1j
)
@
requires_IEEE_754
def
testInitFromFloat
(
self
):
self
.
assertEquals
((
5
,
2
),
_components
(
F
(
2.5
)))
self
.
assertEquals
((
0
,
1
),
_components
(
F
(
-
0.0
)))
self
.
assertEquals
((
3602879701896397
,
36028797018963968
),
_components
(
F
(
0.1
)))
self
.
assertRaises
(
TypeError
,
F
,
float
(
'nan'
))
self
.
assertRaises
(
TypeError
,
F
,
float
(
'inf'
))
self
.
assertRaises
(
TypeError
,
F
,
float
(
'-inf'
))
def
testInitFromDecimal
(
self
):
self
.
assertEquals
((
11
,
10
),
_components
(
F
(
Decimal
(
'1.1'
))))
self
.
assertEquals
((
7
,
200
),
_components
(
F
(
Decimal
(
'3.5e-2'
))))
self
.
assertEquals
((
0
,
1
),
_components
(
F
(
Decimal
(
'.000e20'
))))
self
.
assertRaises
(
TypeError
,
F
,
Decimal
(
'nan'
))
self
.
assertRaises
(
TypeError
,
F
,
Decimal
(
'snan'
))
self
.
assertRaises
(
TypeError
,
F
,
Decimal
(
'inf'
))
self
.
assertRaises
(
TypeError
,
F
,
Decimal
(
'-inf'
))
def
testFromString
(
self
):
def
testFromString
(
self
):
self
.
assertEquals
((
5
,
1
),
_components
(
F
(
"5"
)))
self
.
assertEquals
((
5
,
1
),
_components
(
F
(
"5"
)))
...
...
Misc/NEWS
View file @
7c63eee4
...
@@ -43,6 +43,9 @@ Core and Builtins
...
@@ -43,6 +43,9 @@ Core and Builtins
Library
Library
-------
-------
- Issue #8294: The Fraction constructor now accepts Decimal and float
instances directly.
- Issue #7279: Comparisons involving a Decimal signaling NaN now
- Issue #7279: Comparisons involving a Decimal signaling NaN now
signal InvalidOperation instead of returning False. (Comparisons
signal InvalidOperation instead of returning False. (Comparisons
involving a quiet NaN are unchanged.) Also, Decimal quiet NaNs
involving a quiet NaN are unchanged.) Also, Decimal quiet NaNs
...
...
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