mirror of https://github.com/python/cpython
gh-121798: Add class method Decimal.from_number() (GH-121801)
It is an alternate constructor which only accepts a single numeric argument. Unlike to Decimal.from_float() it accepts also Decimal. Unlike to the standard constructor, it does not accept strings and tuples.
This commit is contained in:
parent
4b358ee647
commit
5217328f93
|
@ -598,6 +598,23 @@ Decimal objects
|
||||||
|
|
||||||
.. versionadded:: 3.1
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
.. classmethod:: from_number(number)
|
||||||
|
|
||||||
|
Alternative constructor that only accepts instances of
|
||||||
|
:class:`float`, :class:`int` or :class:`Decimal`, but not strings
|
||||||
|
or tuples.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> Decimal.from_number(314)
|
||||||
|
Decimal('314')
|
||||||
|
>>> Decimal.from_number(0.1)
|
||||||
|
Decimal('0.1000000000000000055511151231257827021181583404541015625')
|
||||||
|
>>> Decimal.from_number(Decimal('3.14'))
|
||||||
|
Decimal('3.14')
|
||||||
|
|
||||||
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
.. method:: fma(other, third, context=None)
|
.. method:: fma(other, third, context=None)
|
||||||
|
|
||||||
Fused multiply-add. Return self*other+third with no rounding of the
|
Fused multiply-add. Return self*other+third with no rounding of the
|
||||||
|
|
|
@ -239,6 +239,12 @@ ctypes
|
||||||
to help match a non-default ABI.
|
to help match a non-default ABI.
|
||||||
(Contributed by Petr Viktorin in :gh:`97702`.)
|
(Contributed by Petr Viktorin in :gh:`97702`.)
|
||||||
|
|
||||||
|
decimal
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Add alternative :class:`~decimal.Decimal` constructor
|
||||||
|
:meth:`Decimal.from_number() <decimal.Decimal.from_number>`.
|
||||||
|
(Contributed by Serhiy Storchaka in :gh:`121798`.)
|
||||||
|
|
||||||
dis
|
dis
|
||||||
---
|
---
|
||||||
|
|
|
@ -582,6 +582,21 @@ class Decimal(object):
|
||||||
|
|
||||||
raise TypeError("Cannot convert %r to Decimal" % value)
|
raise TypeError("Cannot convert %r to Decimal" % value)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_number(cls, number):
|
||||||
|
"""Converts a real number to a decimal number, exactly.
|
||||||
|
|
||||||
|
>>> Decimal.from_number(314) # int
|
||||||
|
Decimal('314')
|
||||||
|
>>> Decimal.from_number(0.1) # float
|
||||||
|
Decimal('0.1000000000000000055511151231257827021181583404541015625')
|
||||||
|
>>> Decimal.from_number(Decimal('3.14')) # another decimal instance
|
||||||
|
Decimal('3.14')
|
||||||
|
"""
|
||||||
|
if isinstance(number, (int, Decimal, float)):
|
||||||
|
return cls(number)
|
||||||
|
raise TypeError("Cannot convert %r to Decimal" % number)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_float(cls, f):
|
def from_float(cls, f):
|
||||||
"""Converts a float to a decimal number, exactly.
|
"""Converts a float to a decimal number, exactly.
|
||||||
|
|
|
@ -812,6 +812,29 @@ class ExplicitConstructionTest:
|
||||||
x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
|
x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
|
||||||
self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip
|
self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip
|
||||||
|
|
||||||
|
def test_from_number(self, cls=None):
|
||||||
|
Decimal = self.decimal.Decimal
|
||||||
|
if cls is None:
|
||||||
|
cls = Decimal
|
||||||
|
|
||||||
|
def check(arg, expected):
|
||||||
|
d = cls.from_number(arg)
|
||||||
|
self.assertIs(type(d), cls)
|
||||||
|
self.assertEqual(d, expected)
|
||||||
|
|
||||||
|
check(314, Decimal(314))
|
||||||
|
check(3.14, Decimal.from_float(3.14))
|
||||||
|
check(Decimal('3.14'), Decimal('3.14'))
|
||||||
|
self.assertRaises(TypeError, cls.from_number, 3+4j)
|
||||||
|
self.assertRaises(TypeError, cls.from_number, '314')
|
||||||
|
self.assertRaises(TypeError, cls.from_number, (0, (3, 1, 4), 0))
|
||||||
|
self.assertRaises(TypeError, cls.from_number, object())
|
||||||
|
|
||||||
|
def test_from_number_subclass(self, cls=None):
|
||||||
|
class DecimalSubclass(self.decimal.Decimal):
|
||||||
|
pass
|
||||||
|
self.test_from_number(DecimalSubclass)
|
||||||
|
|
||||||
def test_unicode_digits(self):
|
def test_unicode_digits(self):
|
||||||
Decimal = self.decimal.Decimal
|
Decimal = self.decimal.Decimal
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add alternative :class:`~decimal.Decimal` constructor
|
||||||
|
:meth:`Decimal.from_number() <decimal.Decimal.from_number>`.
|
|
@ -2857,6 +2857,51 @@ dec_from_float(PyObject *type, PyObject *pyfloat)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'v' can have any numeric type accepted by the Decimal constructor. Attempt
|
||||||
|
an exact conversion. If the result does not meet the restrictions
|
||||||
|
for an mpd_t, fail with InvalidOperation. */
|
||||||
|
static PyObject *
|
||||||
|
PyDecType_FromNumberExact(PyTypeObject *type, PyObject *v, PyObject *context)
|
||||||
|
{
|
||||||
|
decimal_state *state = get_module_state_by_def(type);
|
||||||
|
assert(v != NULL);
|
||||||
|
if (PyDec_Check(state, v)) {
|
||||||
|
return PyDecType_FromDecimalExact(type, v, context);
|
||||||
|
}
|
||||||
|
else if (PyLong_Check(v)) {
|
||||||
|
return PyDecType_FromLongExact(type, v, context);
|
||||||
|
}
|
||||||
|
else if (PyFloat_Check(v)) {
|
||||||
|
if (dec_addstatus(context, MPD_Float_operation)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyDecType_FromFloatExact(type, v, context);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"conversion from %s to Decimal is not supported",
|
||||||
|
Py_TYPE(v)->tp_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* class method */
|
||||||
|
static PyObject *
|
||||||
|
dec_from_number(PyObject *type, PyObject *number)
|
||||||
|
{
|
||||||
|
PyObject *context;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
decimal_state *state = get_module_state_by_def((PyTypeObject *)type);
|
||||||
|
CURRENT_CONTEXT(state, context);
|
||||||
|
result = PyDecType_FromNumberExact(state->PyDec_Type, number, context);
|
||||||
|
if (type != (PyObject *)state->PyDec_Type && result != NULL) {
|
||||||
|
Py_SETREF(result, PyObject_CallFunctionObjArgs(type, result, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* create_decimal_from_float */
|
/* create_decimal_from_float */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
ctx_from_float(PyObject *context, PyObject *v)
|
ctx_from_float(PyObject *context, PyObject *v)
|
||||||
|
@ -5052,6 +5097,7 @@ static PyMethodDef dec_methods [] =
|
||||||
|
|
||||||
/* Miscellaneous */
|
/* Miscellaneous */
|
||||||
{ "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float },
|
{ "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float },
|
||||||
|
{ "from_number", dec_from_number, METH_O|METH_CLASS, doc_from_number },
|
||||||
{ "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple },
|
{ "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple },
|
||||||
{ "as_integer_ratio", dec_as_integer_ratio, METH_NOARGS, doc_as_integer_ratio },
|
{ "as_integer_ratio", dec_as_integer_ratio, METH_NOARGS, doc_as_integer_ratio },
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,19 @@ Decimal.from_float(0.1) is not the same as Decimal('0.1').\n\
|
||||||
\n\
|
\n\
|
||||||
\n");
|
\n");
|
||||||
|
|
||||||
|
PyDoc_STRVAR(doc_from_number,
|
||||||
|
"from_number($type, number, /)\n--\n\n\
|
||||||
|
Class method that converts a real number to a decimal number, exactly.\n\
|
||||||
|
\n\
|
||||||
|
>>> Decimal.from_number(314) # int\n\
|
||||||
|
Decimal('314')\n\
|
||||||
|
>>> Decimal.from_number(0.1) # float\n\
|
||||||
|
Decimal('0.1000000000000000055511151231257827021181583404541015625')\n\
|
||||||
|
>>> Decimal.from_number(Decimal('3.14')) # another decimal instance\n\
|
||||||
|
Decimal('3.14')\n\
|
||||||
|
\n\
|
||||||
|
\n");
|
||||||
|
|
||||||
PyDoc_STRVAR(doc_fma,
|
PyDoc_STRVAR(doc_fma,
|
||||||
"fma($self, /, other, third, context=None)\n--\n\n\
|
"fma($self, /, other, third, context=None)\n--\n\n\
|
||||||
Fused multiply-add. Return self*other+third with no rounding of the\n\
|
Fused multiply-add. Return self*other+third with no rounding of the\n\
|
||||||
|
|
Loading…
Reference in New Issue