mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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.
This commit is contained in:
parent
5aab44b301
commit
0d250bc119
5 changed files with 22 additions and 45 deletions
|
@ -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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
# 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
|
||||||
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)
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue