mirror of
https://github.com/python/cpython.git
synced 2025-12-15 21:44:50 +00:00
Issue #25928: Add Decimal.as_integer_ratio(). Python parts and docs by
Mark Dickinson.
This commit is contained in:
parent
ac1e7f6983
commit
53f2e0ad45
7 changed files with 213 additions and 3 deletions
|
|
@ -3380,6 +3380,106 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
|
|||
return (PyObject *) pylong;
|
||||
}
|
||||
|
||||
/* Convert a Decimal to its exact integer ratio representation. */
|
||||
static PyObject *
|
||||
dec_as_integer_ratio(PyObject *self, PyObject *args UNUSED)
|
||||
{
|
||||
PyObject *numerator = NULL;
|
||||
PyObject *denominator = NULL;
|
||||
PyObject *exponent = NULL;
|
||||
PyObject *result = NULL;
|
||||
PyObject *tmp;
|
||||
mpd_ssize_t exp;
|
||||
PyObject *context;
|
||||
uint32_t status = 0;
|
||||
PyNumberMethods *long_methods = PyLong_Type.tp_as_number;
|
||||
|
||||
if (mpd_isspecial(MPD(self))) {
|
||||
if (mpd_isnan(MPD(self))) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"cannot convert NaN to integer ratio");
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"cannot convert Infinity to integer ratio");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CURRENT_CONTEXT(context);
|
||||
|
||||
tmp = dec_alloc();
|
||||
if (tmp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!mpd_qcopy(MPD(tmp), MPD(self), &status)) {
|
||||
Py_DECREF(tmp);
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
exp = mpd_iszero(MPD(tmp)) ? 0 : MPD(tmp)->exp;
|
||||
MPD(tmp)->exp = 0;
|
||||
|
||||
/* context and rounding are unused here: the conversion is exact */
|
||||
numerator = dec_as_long(tmp, context, MPD_ROUND_FLOOR);
|
||||
Py_DECREF(tmp);
|
||||
if (numerator == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
exponent = PyLong_FromSsize_t(exp < 0 ? -exp : exp);
|
||||
if (exponent == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
tmp = PyLong_FromLong(10);
|
||||
if (tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
Py_SETREF(exponent, long_methods->nb_power(tmp, exponent, Py_None));
|
||||
Py_DECREF(tmp);
|
||||
if (exponent == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (exp >= 0) {
|
||||
Py_SETREF(numerator, long_methods->nb_multiply(numerator, exponent));
|
||||
if (numerator == NULL) {
|
||||
goto error;
|
||||
}
|
||||
denominator = PyLong_FromLong(1);
|
||||
if (denominator == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
denominator = exponent;
|
||||
exponent = NULL;
|
||||
tmp = _PyLong_GCD(numerator, denominator);
|
||||
if (tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
Py_SETREF(numerator, long_methods->nb_floor_divide(numerator, tmp));
|
||||
Py_SETREF(denominator, long_methods->nb_floor_divide(denominator, tmp));
|
||||
Py_DECREF(tmp);
|
||||
if (numerator == NULL || denominator == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
result = PyTuple_Pack(2, numerator, denominator);
|
||||
|
||||
|
||||
error:
|
||||
Py_XDECREF(exponent);
|
||||
Py_XDECREF(denominator);
|
||||
Py_XDECREF(numerator);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
|
|
@ -4688,6 +4788,7 @@ static PyMethodDef dec_methods [] =
|
|||
/* Miscellaneous */
|
||||
{ "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float },
|
||||
{ "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple },
|
||||
{ "as_integer_ratio", dec_as_integer_ratio, METH_NOARGS, doc_as_integer_ratio },
|
||||
|
||||
/* Special methods */
|
||||
{ "__copy__", dec_copy, METH_NOARGS, NULL },
|
||||
|
|
|
|||
|
|
@ -70,6 +70,15 @@ PyDoc_STRVAR(doc_as_tuple,
|
|||
Return a tuple representation of the number.\n\
|
||||
\n");
|
||||
|
||||
PyDoc_STRVAR(doc_as_integer_ratio,
|
||||
"as_integer_ratio($self, /)\n--\n\n\
|
||||
Decimal.as_integer_ratio() -> (int, int)\n\
|
||||
\n\
|
||||
Return a pair of integers, whose ratio is exactly equal to the original\n\
|
||||
Decimal and with a positive denominator. The ratio is in lowest terms.\n\
|
||||
Raise OverflowError on infinities and a ValueError on NaNs.\n\
|
||||
\n");
|
||||
|
||||
PyDoc_STRVAR(doc_canonical,
|
||||
"canonical($self, /)\n--\n\n\
|
||||
Return the canonical encoding of the argument. Currently, the encoding\n\
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ Functions = {
|
|||
'__abs__', '__bool__', '__ceil__', '__complex__', '__copy__',
|
||||
'__floor__', '__float__', '__hash__', '__int__', '__neg__',
|
||||
'__pos__', '__reduce__', '__repr__', '__str__', '__trunc__',
|
||||
'adjusted', 'as_tuple', 'canonical', 'conjugate', 'copy_abs',
|
||||
'copy_negate', 'is_canonical', 'is_finite', 'is_infinite',
|
||||
'adjusted', 'as_integer_ratio', 'as_tuple', 'canonical', 'conjugate',
|
||||
'copy_abs', 'copy_negate', 'is_canonical', 'is_finite', 'is_infinite',
|
||||
'is_nan', 'is_qnan', 'is_signed', 'is_snan', 'is_zero', 'radix'
|
||||
),
|
||||
# Unary with optional context:
|
||||
|
|
@ -128,7 +128,7 @@ ContextFunctions = {
|
|||
# Functions that require a restricted exponent range for reasonable runtimes.
|
||||
UnaryRestricted = [
|
||||
'__ceil__', '__floor__', '__int__', '__trunc__',
|
||||
'to_integral', 'to_integral_value'
|
||||
'as_integer_ratio', 'to_integral', 'to_integral_value'
|
||||
]
|
||||
|
||||
BinaryRestricted = ['__round__']
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue