mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-53032: support IEEE 754 contexts in the decimal module (#122003)
This was in C version from beginning, but available only on conditional compilation (EXTRA_FUNCTIONALITY). Current patch adds function to create IEEE contexts to the pure-python module as well. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
e20ca6d1b0
commit
5bf0f3666e
7 changed files with 101 additions and 65 deletions
|
@ -1031,6 +1031,14 @@ function to temporarily change the active context.
|
||||||
.. versionchanged:: 3.11
|
.. versionchanged:: 3.11
|
||||||
:meth:`localcontext` now supports setting context attributes through the use of keyword arguments.
|
:meth:`localcontext` now supports setting context attributes through the use of keyword arguments.
|
||||||
|
|
||||||
|
.. function:: IEEEContext(bits)
|
||||||
|
|
||||||
|
Return a context object initialized to the proper values for one of the
|
||||||
|
IEEE interchange formats. The argument must be a multiple of 32 and less
|
||||||
|
than :const:`IEEE_CONTEXT_MAX_BITS`.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
New contexts can also be created using the :class:`Context` constructor
|
New contexts can also be created using the :class:`Context` constructor
|
||||||
described below. In addition, the module provides three pre-made contexts:
|
described below. In addition, the module provides three pre-made contexts:
|
||||||
|
|
||||||
|
@ -1552,18 +1560,19 @@ Constants
|
||||||
The constants in this section are only relevant for the C module. They
|
The constants in this section are only relevant for the C module. They
|
||||||
are also included in the pure Python version for compatibility.
|
are also included in the pure Python version for compatibility.
|
||||||
|
|
||||||
+---------------------+---------------------+-------------------------------+
|
+---------------------------------+---------------------+-------------------------------+
|
||||||
| | 32-bit | 64-bit |
|
| | 32-bit | 64-bit |
|
||||||
+=====================+=====================+===============================+
|
+=================================+=====================+===============================+
|
||||||
| .. data:: MAX_PREC | ``425000000`` | ``999999999999999999`` |
|
| .. data:: MAX_PREC | ``425000000`` | ``999999999999999999`` |
|
||||||
+---------------------+---------------------+-------------------------------+
|
+---------------------------------+---------------------+-------------------------------+
|
||||||
| .. data:: MAX_EMAX | ``425000000`` | ``999999999999999999`` |
|
| .. data:: MAX_EMAX | ``425000000`` | ``999999999999999999`` |
|
||||||
+---------------------+---------------------+-------------------------------+
|
+---------------------------------+---------------------+-------------------------------+
|
||||||
| .. data:: MIN_EMIN | ``-425000000`` | ``-999999999999999999`` |
|
| .. data:: MIN_EMIN | ``-425000000`` | ``-999999999999999999`` |
|
||||||
+---------------------+---------------------+-------------------------------+
|
+---------------------------------+---------------------+-------------------------------+
|
||||||
| .. data:: MIN_ETINY | ``-849999999`` | ``-1999999999999999997`` |
|
| .. data:: MIN_ETINY | ``-849999999`` | ``-1999999999999999997`` |
|
||||||
+---------------------+---------------------+-------------------------------+
|
+---------------------------------+---------------------+-------------------------------+
|
||||||
|
| .. data:: IEEE_CONTEXT_MAX_BITS | ``256`` | ``512`` |
|
||||||
|
+---------------------------------+---------------------+-------------------------------+
|
||||||
|
|
||||||
.. data:: HAVE_THREADS
|
.. data:: HAVE_THREADS
|
||||||
|
|
||||||
|
|
|
@ -750,6 +750,10 @@ decimal
|
||||||
:meth:`Decimal.from_number() <decimal.Decimal.from_number>`.
|
:meth:`Decimal.from_number() <decimal.Decimal.from_number>`.
|
||||||
(Contributed by Serhiy Storchaka in :gh:`121798`.)
|
(Contributed by Serhiy Storchaka in :gh:`121798`.)
|
||||||
|
|
||||||
|
* Expose :func:`decimal.IEEEContext` to support creation of contexts
|
||||||
|
corresponding to the IEEE 754 (2008) decimal interchange formats.
|
||||||
|
(Contributed by Sergey B Kirpichev in :gh:`53032`.)
|
||||||
|
|
||||||
difflib
|
difflib
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,10 @@ __all__ = [
|
||||||
'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', 'ROUND_05UP',
|
'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', 'ROUND_05UP',
|
||||||
|
|
||||||
# Functions for manipulating contexts
|
# Functions for manipulating contexts
|
||||||
'setcontext', 'getcontext', 'localcontext',
|
'setcontext', 'getcontext', 'localcontext', 'IEEEContext',
|
||||||
|
|
||||||
# Limits for the C version for compatibility
|
# Limits for the C version for compatibility
|
||||||
'MAX_PREC', 'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY',
|
'MAX_PREC', 'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY', 'IEEE_CONTEXT_MAX_BITS',
|
||||||
|
|
||||||
# C version: compile time choice that enables the thread local context (deprecated, now always true)
|
# C version: compile time choice that enables the thread local context (deprecated, now always true)
|
||||||
'HAVE_THREADS',
|
'HAVE_THREADS',
|
||||||
|
@ -83,10 +83,12 @@ if sys.maxsize == 2**63-1:
|
||||||
MAX_PREC = 999999999999999999
|
MAX_PREC = 999999999999999999
|
||||||
MAX_EMAX = 999999999999999999
|
MAX_EMAX = 999999999999999999
|
||||||
MIN_EMIN = -999999999999999999
|
MIN_EMIN = -999999999999999999
|
||||||
|
IEEE_CONTEXT_MAX_BITS = 512
|
||||||
else:
|
else:
|
||||||
MAX_PREC = 425000000
|
MAX_PREC = 425000000
|
||||||
MAX_EMAX = 425000000
|
MAX_EMAX = 425000000
|
||||||
MIN_EMIN = -425000000
|
MIN_EMIN = -425000000
|
||||||
|
IEEE_CONTEXT_MAX_BITS = 256
|
||||||
|
|
||||||
MIN_ETINY = MIN_EMIN - (MAX_PREC-1)
|
MIN_ETINY = MIN_EMIN - (MAX_PREC-1)
|
||||||
|
|
||||||
|
@ -417,6 +419,27 @@ def localcontext(ctx=None, **kwargs):
|
||||||
return ctx_manager
|
return ctx_manager
|
||||||
|
|
||||||
|
|
||||||
|
def IEEEContext(bits, /):
|
||||||
|
"""
|
||||||
|
Return a context object initialized to the proper values for one of the
|
||||||
|
IEEE interchange formats. The argument must be a multiple of 32 and less
|
||||||
|
than IEEE_CONTEXT_MAX_BITS.
|
||||||
|
"""
|
||||||
|
if bits <= 0 or bits > IEEE_CONTEXT_MAX_BITS or bits % 32:
|
||||||
|
raise ValueError("argument must be a multiple of 32, "
|
||||||
|
f"with a maximum of {IEEE_CONTEXT_MAX_BITS}")
|
||||||
|
|
||||||
|
ctx = Context()
|
||||||
|
ctx.prec = 9 * (bits//32) - 2
|
||||||
|
ctx.Emax = 3 * (1 << (bits//16 + 3))
|
||||||
|
ctx.Emin = 1 - ctx.Emax
|
||||||
|
ctx.rounding = ROUND_HALF_EVEN
|
||||||
|
ctx.clamp = 1
|
||||||
|
ctx.traps = dict.fromkeys(_signals, False)
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
##### Decimal class #######################################################
|
##### Decimal class #######################################################
|
||||||
|
|
||||||
# Do not subclass Decimal from numbers.Real and do not register it as such
|
# Do not subclass Decimal from numbers.Real and do not register it as such
|
||||||
|
|
|
@ -4399,6 +4399,51 @@ class CContextSubclassing(ContextSubclassing, unittest.TestCase):
|
||||||
class PyContextSubclassing(ContextSubclassing, unittest.TestCase):
|
class PyContextSubclassing(ContextSubclassing, unittest.TestCase):
|
||||||
decimal = P
|
decimal = P
|
||||||
|
|
||||||
|
class IEEEContexts:
|
||||||
|
|
||||||
|
def test_ieee_context(self):
|
||||||
|
# issue 8786: Add support for IEEE 754 contexts to decimal module.
|
||||||
|
IEEEContext = self.decimal.IEEEContext
|
||||||
|
|
||||||
|
def assert_rest(self, context):
|
||||||
|
self.assertEqual(context.clamp, 1)
|
||||||
|
assert_signals(self, context, 'traps', [])
|
||||||
|
assert_signals(self, context, 'flags', [])
|
||||||
|
|
||||||
|
c = IEEEContext(32)
|
||||||
|
self.assertEqual(c.prec, 7)
|
||||||
|
self.assertEqual(c.Emax, 96)
|
||||||
|
self.assertEqual(c.Emin, -95)
|
||||||
|
assert_rest(self, c)
|
||||||
|
|
||||||
|
c = IEEEContext(64)
|
||||||
|
self.assertEqual(c.prec, 16)
|
||||||
|
self.assertEqual(c.Emax, 384)
|
||||||
|
self.assertEqual(c.Emin, -383)
|
||||||
|
assert_rest(self, c)
|
||||||
|
|
||||||
|
c = IEEEContext(128)
|
||||||
|
self.assertEqual(c.prec, 34)
|
||||||
|
self.assertEqual(c.Emax, 6144)
|
||||||
|
self.assertEqual(c.Emin, -6143)
|
||||||
|
assert_rest(self, c)
|
||||||
|
|
||||||
|
# Invalid values
|
||||||
|
self.assertRaises(ValueError, IEEEContext, -1)
|
||||||
|
self.assertRaises(ValueError, IEEEContext, 123)
|
||||||
|
self.assertRaises(ValueError, IEEEContext, 1024)
|
||||||
|
|
||||||
|
def test_constants(self):
|
||||||
|
# IEEEContext
|
||||||
|
IEEE_CONTEXT_MAX_BITS = self.decimal.IEEE_CONTEXT_MAX_BITS
|
||||||
|
self.assertIn(IEEE_CONTEXT_MAX_BITS, {256, 512})
|
||||||
|
|
||||||
|
@requires_cdecimal
|
||||||
|
class CIEEEContexts(IEEEContexts, unittest.TestCase):
|
||||||
|
decimal = C
|
||||||
|
class PyIEEEContexts(IEEEContexts, unittest.TestCase):
|
||||||
|
decimal = P
|
||||||
|
|
||||||
@skip_if_extra_functionality
|
@skip_if_extra_functionality
|
||||||
@requires_cdecimal
|
@requires_cdecimal
|
||||||
class CheckAttributes(unittest.TestCase):
|
class CheckAttributes(unittest.TestCase):
|
||||||
|
@ -4410,6 +4455,7 @@ class CheckAttributes(unittest.TestCase):
|
||||||
self.assertEqual(C.MAX_EMAX, P.MAX_EMAX)
|
self.assertEqual(C.MAX_EMAX, P.MAX_EMAX)
|
||||||
self.assertEqual(C.MIN_EMIN, P.MIN_EMIN)
|
self.assertEqual(C.MIN_EMIN, P.MIN_EMIN)
|
||||||
self.assertEqual(C.MIN_ETINY, P.MIN_ETINY)
|
self.assertEqual(C.MIN_ETINY, P.MIN_ETINY)
|
||||||
|
self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, P.IEEE_CONTEXT_MAX_BITS)
|
||||||
|
|
||||||
self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False)
|
self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False)
|
||||||
self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False)
|
self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False)
|
||||||
|
@ -4893,42 +4939,6 @@ class PyWhitebox(unittest.TestCase):
|
||||||
class CFunctionality(unittest.TestCase):
|
class CFunctionality(unittest.TestCase):
|
||||||
"""Extra functionality in _decimal"""
|
"""Extra functionality in _decimal"""
|
||||||
|
|
||||||
@requires_extra_functionality
|
|
||||||
def test_c_ieee_context(self):
|
|
||||||
# issue 8786: Add support for IEEE 754 contexts to decimal module.
|
|
||||||
IEEEContext = C.IEEEContext
|
|
||||||
DECIMAL32 = C.DECIMAL32
|
|
||||||
DECIMAL64 = C.DECIMAL64
|
|
||||||
DECIMAL128 = C.DECIMAL128
|
|
||||||
|
|
||||||
def assert_rest(self, context):
|
|
||||||
self.assertEqual(context.clamp, 1)
|
|
||||||
assert_signals(self, context, 'traps', [])
|
|
||||||
assert_signals(self, context, 'flags', [])
|
|
||||||
|
|
||||||
c = IEEEContext(DECIMAL32)
|
|
||||||
self.assertEqual(c.prec, 7)
|
|
||||||
self.assertEqual(c.Emax, 96)
|
|
||||||
self.assertEqual(c.Emin, -95)
|
|
||||||
assert_rest(self, c)
|
|
||||||
|
|
||||||
c = IEEEContext(DECIMAL64)
|
|
||||||
self.assertEqual(c.prec, 16)
|
|
||||||
self.assertEqual(c.Emax, 384)
|
|
||||||
self.assertEqual(c.Emin, -383)
|
|
||||||
assert_rest(self, c)
|
|
||||||
|
|
||||||
c = IEEEContext(DECIMAL128)
|
|
||||||
self.assertEqual(c.prec, 34)
|
|
||||||
self.assertEqual(c.Emax, 6144)
|
|
||||||
self.assertEqual(c.Emin, -6143)
|
|
||||||
assert_rest(self, c)
|
|
||||||
|
|
||||||
# Invalid values
|
|
||||||
self.assertRaises(OverflowError, IEEEContext, 2**63)
|
|
||||||
self.assertRaises(ValueError, IEEEContext, -1)
|
|
||||||
self.assertRaises(ValueError, IEEEContext, 1024)
|
|
||||||
|
|
||||||
@requires_extra_functionality
|
@requires_extra_functionality
|
||||||
def test_c_context(self):
|
def test_c_context(self):
|
||||||
Context = C.Context
|
Context = C.Context
|
||||||
|
@ -4949,12 +4959,6 @@ class CFunctionality(unittest.TestCase):
|
||||||
C.DecSubnormal, C.DecUnderflow
|
C.DecSubnormal, C.DecUnderflow
|
||||||
)
|
)
|
||||||
|
|
||||||
# IEEEContext
|
|
||||||
self.assertEqual(C.DECIMAL32, 32)
|
|
||||||
self.assertEqual(C.DECIMAL64, 64)
|
|
||||||
self.assertEqual(C.DECIMAL128, 128)
|
|
||||||
self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, 512)
|
|
||||||
|
|
||||||
# Conditions
|
# Conditions
|
||||||
for i, v in enumerate(cond):
|
for i, v in enumerate(cond):
|
||||||
self.assertEqual(v, 1<<i)
|
self.assertEqual(v, 1<<i)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Expose :func:`decimal.IEEEContext` to support creation of contexts
|
||||||
|
corresponding to the IEEE 754 (2008) decimal interchange formats.
|
||||||
|
Patch by Sergey B Kirpichev.
|
|
@ -1557,7 +1557,6 @@ init_extended_context(PyObject *v)
|
||||||
CtxCaps(v) = 1;
|
CtxCaps(v) = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EXTRA_FUNCTIONALITY
|
|
||||||
/* Factory function for creating IEEE interchange format contexts */
|
/* Factory function for creating IEEE interchange format contexts */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
ieee_context(PyObject *module, PyObject *v)
|
ieee_context(PyObject *module, PyObject *v)
|
||||||
|
@ -1593,7 +1592,6 @@ error:
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
context_copy(PyObject *self, PyObject *Py_UNUSED(dummy))
|
context_copy(PyObject *self, PyObject *Py_UNUSED(dummy))
|
||||||
|
@ -5886,9 +5884,7 @@ static PyMethodDef _decimal_methods [] =
|
||||||
{ "getcontext", PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext},
|
{ "getcontext", PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext},
|
||||||
{ "setcontext", PyDec_SetCurrentContext, METH_O, doc_setcontext},
|
{ "setcontext", PyDec_SetCurrentContext, METH_O, doc_setcontext},
|
||||||
{ "localcontext", _PyCFunction_CAST(ctxmanager_new), METH_VARARGS|METH_KEYWORDS, doc_localcontext},
|
{ "localcontext", _PyCFunction_CAST(ctxmanager_new), METH_VARARGS|METH_KEYWORDS, doc_localcontext},
|
||||||
#ifdef EXTRA_FUNCTIONALITY
|
|
||||||
{ "IEEEContext", ieee_context, METH_O, doc_ieee_context},
|
{ "IEEEContext", ieee_context, METH_O, doc_ieee_context},
|
||||||
#endif
|
|
||||||
{ NULL, NULL, 1, NULL }
|
{ NULL, NULL, 1, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5905,11 +5901,11 @@ static struct ssize_constmap ssize_constants [] = {
|
||||||
struct int_constmap { const char *name; int val; };
|
struct int_constmap { const char *name; int val; };
|
||||||
static struct int_constmap int_constants [] = {
|
static struct int_constmap int_constants [] = {
|
||||||
/* int constants */
|
/* int constants */
|
||||||
|
{"IEEE_CONTEXT_MAX_BITS", MPD_IEEE_CONTEXT_MAX_BITS},
|
||||||
#ifdef EXTRA_FUNCTIONALITY
|
#ifdef EXTRA_FUNCTIONALITY
|
||||||
{"DECIMAL32", MPD_DECIMAL32},
|
{"DECIMAL32", MPD_DECIMAL32},
|
||||||
{"DECIMAL64", MPD_DECIMAL64},
|
{"DECIMAL64", MPD_DECIMAL64},
|
||||||
{"DECIMAL128", MPD_DECIMAL128},
|
{"DECIMAL128", MPD_DECIMAL128},
|
||||||
{"IEEE_CONTEXT_MAX_BITS", MPD_IEEE_CONTEXT_MAX_BITS},
|
|
||||||
/* int condition flags */
|
/* int condition flags */
|
||||||
{"DecClamped", MPD_Clamped},
|
{"DecClamped", MPD_Clamped},
|
||||||
{"DecConversionSyntax", MPD_Conversion_syntax},
|
{"DecConversionSyntax", MPD_Conversion_syntax},
|
||||||
|
|
|
@ -37,15 +37,12 @@ exiting the with-statement. If no context is specified, a copy of the current\n\
|
||||||
default context is used.\n\
|
default context is used.\n\
|
||||||
\n");
|
\n");
|
||||||
|
|
||||||
#ifdef EXTRA_FUNCTIONALITY
|
|
||||||
PyDoc_STRVAR(doc_ieee_context,
|
PyDoc_STRVAR(doc_ieee_context,
|
||||||
"IEEEContext($module, bits, /)\n--\n\n\
|
"IEEEContext($module, bits, /)\n--\n\n\
|
||||||
Return a context object initialized to the proper values for one of the\n\
|
Return a context object initialized to the proper values for one of the\n\
|
||||||
IEEE interchange formats. The argument must be a multiple of 32 and less\n\
|
IEEE interchange formats. The argument must be a multiple of 32 and less\n\
|
||||||
than IEEE_CONTEXT_MAX_BITS. For the most common values, the constants\n\
|
than IEEE_CONTEXT_MAX_BITS.\n\
|
||||||
DECIMAL32, DECIMAL64 and DECIMAL128 are provided.\n\
|
|
||||||
\n");
|
\n");
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue