Commit 0d250bc1 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #25971: Optimized creating Fractions from floats by 2 times and from

Decimals by 3 times.
Unified error messages in float.as_integer_ratio(), Decimal.as_integer_ratio(),
and Fraction constructors.
parent 5aab44b3
...@@ -1026,11 +1026,9 @@ class Decimal(object): ...@@ -1026,11 +1026,9 @@ class Decimal(object):
""" """
if self._is_special: if self._is_special:
if self.is_nan(): if self.is_nan():
raise ValueError("Cannot pass NaN " raise ValueError("cannot convert NaN to integer ratio")
"to decimal.as_integer_ratio.")
else: else:
raise OverflowError("Cannot pass infinity " raise OverflowError("cannot convert Infinity to integer ratio")
"to decimal.as_integer_ratio.")
if not self: if not self:
return 0, 1 return 0, 1
......
...@@ -125,17 +125,9 @@ class Fraction(numbers.Rational): ...@@ -125,17 +125,9 @@ class Fraction(numbers.Rational):
self._denominator = numerator.denominator self._denominator = numerator.denominator
return self return self
elif isinstance(numerator, float): elif isinstance(numerator, (float, Decimal)):
# Exact conversion from float # Exact conversion
value = Fraction.from_float(numerator) self._numerator, self._denominator = numerator.as_integer_ratio()
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 return self
elif isinstance(numerator, str): elif isinstance(numerator, str):
...@@ -210,10 +202,6 @@ class Fraction(numbers.Rational): ...@@ -210,10 +202,6 @@ class Fraction(numbers.Rational):
elif not isinstance(f, float): elif not isinstance(f, float):
raise TypeError("%s.from_float() only takes floats, not %r (%s)" % raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
(cls.__name__, f, type(f).__name__)) (cls.__name__, f, type(f).__name__))
if math.isnan(f):
raise ValueError("Cannot convert %r to %s." % (f, cls.__name__))
if math.isinf(f):
raise OverflowError("Cannot convert %r to %s." % (f, cls.__name__))
return cls(*f.as_integer_ratio()) return cls(*f.as_integer_ratio())
@classmethod @classmethod
...@@ -226,19 +214,7 @@ class Fraction(numbers.Rational): ...@@ -226,19 +214,7 @@ class Fraction(numbers.Rational):
raise TypeError( raise TypeError(
"%s.from_decimal() only takes Decimals, not %r (%s)" % "%s.from_decimal() only takes Decimals, not %r (%s)" %
(cls.__name__, dec, type(dec).__name__)) (cls.__name__, dec, type(dec).__name__))
if dec.is_infinite(): return cls(*dec.as_integer_ratio())
raise OverflowError(
"Cannot convert %s to %s." % (dec, cls.__name__))
if dec.is_nan():
raise ValueError("Cannot convert %s to %s." % (dec, cls.__name__))
sign, digits, exp = dec.as_tuple()
digits = int(''.join(map(str, digits)))
if sign:
digits = -digits
if exp >= 0:
return cls(digits * 10 ** exp)
else:
return cls(digits, 10 ** -exp)
def limit_denominator(self, max_denominator=1000000): def limit_denominator(self, max_denominator=1000000):
"""Closest Fraction to self with denominator at most max_denominator. """Closest Fraction to self with denominator at most max_denominator.
......
...@@ -263,13 +263,13 @@ class FractionTest(unittest.TestCase): ...@@ -263,13 +263,13 @@ class FractionTest(unittest.TestCase):
nan = inf - inf nan = inf - inf
# bug 16469: error types should be consistent with float -> int # bug 16469: error types should be consistent with float -> int
self.assertRaisesMessage( self.assertRaisesMessage(
OverflowError, "Cannot convert inf to Fraction.", OverflowError, "cannot convert Infinity to integer ratio",
F.from_float, inf) F.from_float, inf)
self.assertRaisesMessage( self.assertRaisesMessage(
OverflowError, "Cannot convert -inf to Fraction.", OverflowError, "cannot convert Infinity to integer ratio",
F.from_float, -inf) F.from_float, -inf)
self.assertRaisesMessage( self.assertRaisesMessage(
ValueError, "Cannot convert nan to Fraction.", ValueError, "cannot convert NaN to integer ratio",
F.from_float, nan) F.from_float, nan)
def testFromDecimal(self): def testFromDecimal(self):
...@@ -284,16 +284,16 @@ class FractionTest(unittest.TestCase): ...@@ -284,16 +284,16 @@ class FractionTest(unittest.TestCase):
# bug 16469: error types should be consistent with decimal -> int # bug 16469: error types should be consistent with decimal -> int
self.assertRaisesMessage( self.assertRaisesMessage(
OverflowError, "Cannot convert Infinity to Fraction.", OverflowError, "cannot convert Infinity to integer ratio",
F.from_decimal, Decimal("inf")) F.from_decimal, Decimal("inf"))
self.assertRaisesMessage( self.assertRaisesMessage(
OverflowError, "Cannot convert -Infinity to Fraction.", OverflowError, "cannot convert Infinity to integer ratio",
F.from_decimal, Decimal("-inf")) F.from_decimal, Decimal("-inf"))
self.assertRaisesMessage( self.assertRaisesMessage(
ValueError, "Cannot convert NaN to Fraction.", ValueError, "cannot convert NaN to integer ratio",
F.from_decimal, Decimal("nan")) F.from_decimal, Decimal("nan"))
self.assertRaisesMessage( self.assertRaisesMessage(
ValueError, "Cannot convert sNaN to Fraction.", ValueError, "cannot convert NaN to integer ratio",
F.from_decimal, Decimal("snan")) F.from_decimal, Decimal("snan"))
def testLimitDenominator(self): def testLimitDenominator(self):
......
...@@ -126,6 +126,9 @@ Core and Builtins ...@@ -126,6 +126,9 @@ Core and Builtins
Library Library
------- -------
- Issue #25971: Optimized creating Fractions from floats by 2 times and from
Decimals by 3 times.
- Issue #25802: Document as deprecated the remaining implementations of - Issue #25802: Document as deprecated the remaining implementations of
importlib.abc.Loader.load_module(). importlib.abc.Loader.load_module().
......
...@@ -1466,14 +1466,14 @@ float_as_integer_ratio(PyObject *v, PyObject *unused) ...@@ -1466,14 +1466,14 @@ float_as_integer_ratio(PyObject *v, PyObject *unused)
CONVERT_TO_DOUBLE(v, self); CONVERT_TO_DOUBLE(v, self);
if (Py_IS_INFINITY(self)) { if (Py_IS_INFINITY(self)) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"Cannot pass infinity to float.as_integer_ratio."); "cannot convert Infinity to integer ratio");
return NULL; return NULL;
} }
if (Py_IS_NAN(self)) { if (Py_IS_NAN(self)) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"Cannot pass NaN to float.as_integer_ratio."); "cannot convert NaN to integer ratio");
return NULL; return NULL;
} }
PyFPE_START_PROTECT("as_integer_ratio", goto error); PyFPE_START_PROTECT("as_integer_ratio", goto error);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment