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.
.. class:: Fraction(numerator=0, denominator=1)
Fraction(other_fraction)
Fraction(float)
Fraction(decimal)
Fraction(string)
The first version requires that *numerator* and *denominator* are
instances of :class:`numbers.Rational` and returns a new
:class:`Fraction` instance with value ``numerator/denominator``. If
*denominator* is :const:`0`, it raises a
:exc:`ZeroDivisionError`. The second version requires that
*other_fraction* is an instance of :class:`numbers.Rational` and
returns an :class:`Fraction` instance with the same value. The
last version of the constructor expects a string or unicode
instance. The usual form for this instance is::
The first version requires that *numerator* and *denominator* are instances
of :class:`numbers.Rational` and returns a new :class:`Fraction` instance
with value ``numerator/denominator``. If *denominator* is :const:`0`, it
raises a :exc:`ZeroDivisionError`. The second version requires that
*other_fraction* is an instance of :class:`numbers.Rational` and returns a
:class:`Fraction` instance with the same value. The next two versions accept
either a :class:`float` or a :class:`decimal.Decimal` instance, and return a
:class:`Fraction` instance with exactly the same value. Note that due to the
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]
...
...
@@ -57,6 +64,13 @@ another rational number, or from a string.
Fraction(-1, 8)
>>> Fraction('7e-6')
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
...
...
@@ -65,6 +79,10 @@ another rational number, or from a string.
and should be treated as immutable. In addition,
: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)
...
...
@@ -72,12 +90,19 @@ another rational number, or from a string.
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)``
.. note:: From Python 2.7 onwards, you can also construct a
:class:`Fraction` instance directly from a :class:`float`.
.. method:: from_decimal(dec)
This class method constructs a :class:`Fraction` representing the exact
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)
...
...
@@ -92,10 +117,12 @@ another rational number, or from a string.
or for recovering a rational number that's represented as a float:
>>> from math import pi, cos
>>> Fraction
.from_float
(cos(pi/3))
>>> Fraction(cos(pi/3))
Fraction(4503599627370497, 9007199254740992)
>>> Fraction
.from_float
(cos(pi/3)).limit_denominator()
>>> Fraction(cos(pi/3)).limit_denominator()
Fraction(1, 2)
>>> Fraction(1.1).limit_denominator()
Fraction(11, 10)
.. function:: gcd(a, b)
...
...
Lib/fractions.py
View file @
7c63eee4
...
...
@@ -4,6 +4,7 @@
"""Rational, infinite-precision, real numbers."""
from
__future__
import
division
from
decimal
import
Decimal
import
math
import
numbers
import
operator
...
...
@@ -43,13 +44,21 @@ _RATIONAL_FORMAT = re.compile(r"""
class
Fraction
(
Rational
):
"""This class implements rational numbers.
Fraction(8, 6) will produce a rational number equivalent to
4/3. Both arguments must be Integral. The numerator defaults to 0
and the denominator defaults to 1 so that Fraction(3) == 3 and
Fraction() == 0.
In the two-argument form of the constructor, Fraction(8, 6) will
produce a rational number equivalent to 4/3. Both arguments must
be Rational. The numerator defaults to 0 and the denominator
defaults to 1 so that Fraction(3) == 3 and
Fraction() == 0.
Fractions can also be constructed from strings of the form
'[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
Fractions can also be constructed from:
- 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):
def
__new__
(
cls
,
numerator
=
0
,
denominator
=
None
):
"""Constructs a Fraction.
Takes a string like '3/2' or '1.5', another Fraction, or a
numerator/denominator pair.
Takes a string like '3/2' or '1.5', another Rational instance, a
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
)
...
...
@@ -71,6 +104,19 @@ class Fraction(Rational):
self
.
_denominator
=
numerator
.
denominator
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
):
# Handle construction from strings.
m
=
_RATIONAL_FORMAT
.
match
(
numerator
)
...
...
Lib/test/test_fractions.py
View file @
7c63eee4
...
...
@@ -12,6 +12,11 @@ from cPickle import dumps, loads
F
=
fractions
.
Fraction
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
):
"""Dummy float class for testing comparisons with Fractions"""
...
...
@@ -137,13 +142,33 @@ class FractionTest(unittest.TestCase):
self
.
assertRaisesMessage
(
ZeroDivisionError
,
"Fraction(12, 0)"
,
F
,
12
,
0
)
self
.
assertRaises
(
TypeError
,
F
,
1.5
)
self
.
assertRaises
(
TypeError
,
F
,
1.5
+
3j
)
self
.
assertRaises
(
TypeError
,
F
,
"3/2"
,
3
)
self
.
assertRaises
(
TypeError
,
F
,
3
,
0j
)
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
):
self
.
assertEquals
((
5
,
1
),
_components
(
F
(
"5"
)))
...
...
Misc/NEWS
View file @
7c63eee4
...
...
@@ -43,6 +43,9 @@ Core and Builtins
Library
-------
- Issue #8294: The Fraction constructor now accepts Decimal and float
instances directly.
- Issue #7279: Comparisons involving a Decimal signaling NaN now
signal InvalidOperation instead of returning False. (Comparisons
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