mirror of https://github.com/python/cpython
Issue #5812: make Fraction('1e-6') valid. Backport of r71806.
This commit is contained in:
parent
ebafbb705c
commit
8100bd8431
|
@ -27,20 +27,17 @@ another rational number, or from a string.
|
||||||
*other_fraction* is an instance of :class:`numbers.Rational` and
|
*other_fraction* is an instance of :class:`numbers.Rational` and
|
||||||
returns an :class:`Fraction` instance with the same value. The
|
returns an :class:`Fraction` instance with the same value. The
|
||||||
last version of the constructor expects a string or unicode
|
last version of the constructor expects a string or unicode
|
||||||
instance in one of two possible forms. The first form is::
|
instance. The usual form for this instance is::
|
||||||
|
|
||||||
[sign] numerator ['/' denominator]
|
[sign] numerator ['/' denominator]
|
||||||
|
|
||||||
where the optional ``sign`` may be either '+' or '-' and
|
where the optional ``sign`` may be either '+' or '-' and
|
||||||
``numerator`` and ``denominator`` (if present) are strings of
|
``numerator`` and ``denominator`` (if present) are strings of
|
||||||
decimal digits. The second permitted form is that of a number
|
decimal digits. In addition, any string that represents a finite
|
||||||
containing a decimal point::
|
value and is accepted by the :class:`float` constructor is also
|
||||||
|
accepted by the :class:`Fraction` constructor. In either form the
|
||||||
[sign] integer '.' [fraction] | [sign] '.' fraction
|
input string may also have leading and/or trailing whitespace.
|
||||||
|
Here are some examples::
|
||||||
where ``integer`` and ``fraction`` are strings of digits. In
|
|
||||||
either form the input string may also have leading and/or trailing
|
|
||||||
whitespace. Here are some examples::
|
|
||||||
|
|
||||||
>>> from fractions import Fraction
|
>>> from fractions import Fraction
|
||||||
>>> Fraction(16, -10)
|
>>> Fraction(16, -10)
|
||||||
|
@ -58,6 +55,8 @@ another rational number, or from a string.
|
||||||
Fraction(1414213, 1000000)
|
Fraction(1414213, 1000000)
|
||||||
>>> Fraction('-.125')
|
>>> Fraction('-.125')
|
||||||
Fraction(-1, 8)
|
Fraction(-1, 8)
|
||||||
|
>>> Fraction('7e-6')
|
||||||
|
Fraction(7, 1000000)
|
||||||
|
|
||||||
|
|
||||||
The :class:`Fraction` class inherits from the abstract base class
|
The :class:`Fraction` class inherits from the abstract base class
|
||||||
|
|
|
@ -30,13 +30,14 @@ _RATIONAL_FORMAT = re.compile(r"""
|
||||||
(?P<sign>[-+]?) # an optional sign, then
|
(?P<sign>[-+]?) # an optional sign, then
|
||||||
(?=\d|\.\d) # lookahead for digit or .digit
|
(?=\d|\.\d) # lookahead for digit or .digit
|
||||||
(?P<num>\d*) # numerator (possibly empty)
|
(?P<num>\d*) # numerator (possibly empty)
|
||||||
(?: # followed by an optional
|
(?: # followed by
|
||||||
/(?P<denom>\d+) # / and denominator
|
(?:/(?P<denom>\d+))? # an optional denominator
|
||||||
| # or
|
| # or
|
||||||
\.(?P<decimal>\d*) # decimal point and fractional part
|
(?:\.(?P<decimal>\d*))? # an optional fractional part
|
||||||
)?
|
(?:E(?P<exp>[-+]?\d+))? # and optional exponent
|
||||||
|
)
|
||||||
\s*\Z # and optional whitespace to finish
|
\s*\Z # and optional whitespace to finish
|
||||||
""", re.VERBOSE)
|
""", re.VERBOSE | re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
class Fraction(Rational):
|
class Fraction(Rational):
|
||||||
|
@ -67,22 +68,28 @@ class Fraction(Rational):
|
||||||
if type(numerator) not in (int, long) and denominator == 1:
|
if type(numerator) not in (int, long) and denominator == 1:
|
||||||
if isinstance(numerator, basestring):
|
if isinstance(numerator, basestring):
|
||||||
# Handle construction from strings.
|
# Handle construction from strings.
|
||||||
input = numerator
|
m = _RATIONAL_FORMAT.match(numerator)
|
||||||
m = _RATIONAL_FORMAT.match(input)
|
|
||||||
if m is None:
|
if m is None:
|
||||||
raise ValueError('Invalid literal for Fraction: %r' % input)
|
raise ValueError('Invalid literal for Fraction: %r' %
|
||||||
numerator = m.group('num')
|
numerator)
|
||||||
|
numerator = int(m.group('num') or '0')
|
||||||
|
denom = m.group('denom')
|
||||||
|
if denom:
|
||||||
|
denominator = int(denom)
|
||||||
|
else:
|
||||||
|
denominator = 1
|
||||||
decimal = m.group('decimal')
|
decimal = m.group('decimal')
|
||||||
if decimal:
|
if decimal:
|
||||||
# The literal is a decimal number.
|
scale = 10**len(decimal)
|
||||||
numerator = int(numerator + decimal)
|
numerator = numerator * scale + int(decimal)
|
||||||
denominator = 10**len(decimal)
|
denominator *= scale
|
||||||
|
exp = m.group('exp')
|
||||||
|
if exp:
|
||||||
|
exp = int(exp)
|
||||||
|
if exp >= 0:
|
||||||
|
numerator *= 10**exp
|
||||||
else:
|
else:
|
||||||
# The literal is an integer or fraction.
|
denominator *= 10**-exp
|
||||||
numerator = int(numerator)
|
|
||||||
# Default denominator to 1.
|
|
||||||
denominator = int(m.group('denom') or 1)
|
|
||||||
|
|
||||||
if m.group('sign') == '-':
|
if m.group('sign') == '-':
|
||||||
numerator = -numerator
|
numerator = -numerator
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,11 @@ class FractionTest(unittest.TestCase):
|
||||||
self.assertEquals((-16, 5), _components(F(u" -3.2 ")))
|
self.assertEquals((-16, 5), _components(F(u" -3.2 ")))
|
||||||
self.assertEquals((-3, 1), _components(F(u" -3. ")))
|
self.assertEquals((-3, 1), _components(F(u" -3. ")))
|
||||||
self.assertEquals((3, 5), _components(F(u" .6 ")))
|
self.assertEquals((3, 5), _components(F(u" .6 ")))
|
||||||
|
self.assertEquals((1, 3125), _components(F("32.e-5")))
|
||||||
|
self.assertEquals((1000000, 1), _components(F("1E+06")))
|
||||||
|
self.assertEquals((-12300, 1), _components(F("-1.23e4")))
|
||||||
|
self.assertEquals((0, 1), _components(F(" .0e+0\t")))
|
||||||
|
self.assertEquals((0, 1), _components(F("-0.000e0")))
|
||||||
|
|
||||||
|
|
||||||
self.assertRaisesMessage(
|
self.assertRaisesMessage(
|
||||||
|
@ -88,6 +93,9 @@ class FractionTest(unittest.TestCase):
|
||||||
self.assertRaisesMessage(
|
self.assertRaisesMessage(
|
||||||
ValueError, "Invalid literal for Fraction: '3/'",
|
ValueError, "Invalid literal for Fraction: '3/'",
|
||||||
F, "3/")
|
F, "3/")
|
||||||
|
self.assertRaisesMessage(
|
||||||
|
ValueError, "Invalid literal for Fraction: '/2'",
|
||||||
|
F, "/2")
|
||||||
self.assertRaisesMessage(
|
self.assertRaisesMessage(
|
||||||
ValueError, "Invalid literal for Fraction: '3 /2'",
|
ValueError, "Invalid literal for Fraction: '3 /2'",
|
||||||
F, "3 /2")
|
F, "3 /2")
|
||||||
|
@ -103,10 +111,6 @@ class FractionTest(unittest.TestCase):
|
||||||
# Avoid treating '.' as a regex special character.
|
# Avoid treating '.' as a regex special character.
|
||||||
ValueError, "Invalid literal for Fraction: '3a2'",
|
ValueError, "Invalid literal for Fraction: '3a2'",
|
||||||
F, "3a2")
|
F, "3a2")
|
||||||
self.assertRaisesMessage(
|
|
||||||
# Only parse ordinary decimals, not scientific form.
|
|
||||||
ValueError, "Invalid literal for Fraction: '3.2e4'",
|
|
||||||
F, "3.2e4")
|
|
||||||
self.assertRaisesMessage(
|
self.assertRaisesMessage(
|
||||||
# Don't accept combinations of decimals and fractions.
|
# Don't accept combinations of decimals and fractions.
|
||||||
ValueError, "Invalid literal for Fraction: '3/7.2'",
|
ValueError, "Invalid literal for Fraction: '3/7.2'",
|
||||||
|
|
|
@ -241,6 +241,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #5812: Fraction('1e6') is valid: more generally, any string
|
||||||
|
that's valid for float() is now valid for Fraction(), with the
|
||||||
|
exception of strings representing NaNs and infinities.
|
||||||
|
|
||||||
- Issue #5795: Fixed test_distutils failure on Debian ppc.
|
- Issue #5795: Fixed test_distutils failure on Debian ppc.
|
||||||
|
|
||||||
- Issue #5768: Fixed bug in Unicode output logic and test case for same.
|
- Issue #5768: Fixed bug in Unicode output logic and test case for same.
|
||||||
|
|
Loading…
Reference in New Issue