| .. sectionauthor:: Stefan Krah |
| |
| .. highlight:: c |
| |
| |
| Decimal capsule API |
| =================== |
| |
| Capsule API functions can be used in the same manner as regular library |
| functions, provided that the API has been initialized. |
| |
| |
| Initialize |
| ---------- |
| |
| Typically, a C extension module that uses the decimal API will do these |
| steps in its init function: |
| |
| .. code-block:: c |
| |
| #include "pydecimal.h" |
| |
| static int decimal_initialized = 0; |
| if (!decimal_initialized) { |
| if (import_decimal() < 0) { |
| return NULL; |
| } |
| |
| decimal_initialized = 1; |
| } |
| |
| |
| Type checking, predicates, accessors |
| ------------------------------------ |
| |
| .. c:function:: int PyDec_TypeCheck(const PyObject *dec) |
| |
| Return 1 if ``dec`` is a Decimal, 0 otherwise. This function does not set |
| any exceptions. |
| |
| |
| .. c:function:: int PyDec_IsSpecial(const PyObject *dec) |
| |
| Return 1 if ``dec`` is ``NaN``, ``sNaN`` or ``Infinity``, 0 otherwise. |
| |
| Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| this is the only failure mode, so if ``dec`` has already been type-checked, no |
| errors can occur and the function can be treated as a simple predicate. |
| |
| |
| .. c:function:: int PyDec_IsNaN(const PyObject *dec) |
| |
| Return 1 if ``dec`` is ``NaN`` or ``sNaN``, 0 otherwise. |
| |
| Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| this is the only failure mode, so if ``dec`` has already been type-checked, no |
| errors can occur and the function can be treated as a simple predicate. |
| |
| |
| .. c:function:: int PyDec_IsInfinite(const PyObject *dec) |
| |
| Return 1 if ``dec`` is ``Infinity``, 0 otherwise. |
| |
| Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| this is the only failure mode, so if ``dec`` has already been type-checked, no |
| errors can occur and the function can be treated as a simple predicate. |
| |
| |
| .. c:function:: int64_t PyDec_GetDigits(const PyObject *dec) |
| |
| Return the number of digits in the coefficient. For ``Infinity``, the |
| number of digits is always zero. Typically, the same applies to ``NaN`` |
| and ``sNaN``, but both of these can have a payload that is equivalent to |
| a coefficient. Therefore, ``NaNs`` can have a nonzero return value. |
| |
| Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| this is the only failure mode, so if ``dec`` has already been type-checked, no |
| errors can occur and the function can be treated as a simple accessor. |
| |
| |
| Exact conversions between decimals and primitive C types |
| -------------------------------------------------------- |
| |
| This API supports conversions for decimals with a coefficient up to 38 digits. |
| |
| Data structures |
| ~~~~~~~~~~~~~~~ |
| |
| The conversion functions use the following status codes and data structures: |
| |
| .. code-block:: c |
| |
| /* status cases for getting a triple */ |
| enum mpd_triple_class { |
| MPD_TRIPLE_NORMAL, |
| MPD_TRIPLE_INF, |
| MPD_TRIPLE_QNAN, |
| MPD_TRIPLE_SNAN, |
| MPD_TRIPLE_ERROR, |
| }; |
| |
| typedef struct { |
| enum mpd_triple_class tag; |
| uint8_t sign; |
| uint64_t hi; |
| uint64_t lo; |
| int64_t exp; |
| } mpd_uint128_triple_t; |
| |
| The status cases are explained below. ``sign`` is 0 for positive and 1 for negative. |
| ``((uint128_t)hi << 64) + lo`` is the coefficient, ``exp`` is the exponent. |
| |
| The data structure is called "triple" because the decimal triple (sign, coeff, exp) |
| is an established term and (``hi``, ``lo``) represents a single ``uint128_t`` coefficient. |
| |
| |
| Functions |
| ~~~~~~~~~ |
| |
| .. c:function:: mpd_uint128_triple_t PyDec_AsUint128Triple(const PyObject *dec) |
| |
| Convert a decimal to a triple. As above, it is guaranteed that the only |
| Python failure mode is a TypeError, checks can be omitted if the type is |
| known. |
| |
| For simplicity, the usage of the function and all special cases are |
| explained in code form and comments: |
| |
| .. code-block:: c |
| |
| triple = PyDec_AsUint128Triple(dec); |
| switch (triple.tag) { |
| case MPD_TRIPLE_QNAN: |
| /* |
| * Success: handle a quiet NaN. |
| * 1) triple.sign is 0 or 1. |
| * 2) triple.exp is always 0. |
| * 3) If triple.hi or triple.lo are nonzero, the NaN has a payload. |
| */ |
| break; |
| |
| case MPD_TRIPLE_SNAN: |
| /* |
| * Success: handle a signaling NaN. |
| * 1) triple.sign is 0 or 1. |
| * 2) triple.exp is always 0. |
| * 3) If triple.hi or triple.lo are nonzero, the sNaN has a payload. |
| */ |
| break; |
| |
| case MPD_TRIPLE_INF: |
| /* |
| * Success: handle Infinity. |
| * 1) triple.sign is 0 or 1. |
| * 2) triple.exp is always 0. |
| * 3) triple.hi and triple.lo are always zero. |
| */ |
| break; |
| |
| case MPD_TRIPLE_NORMAL: |
| /* Success: handle a finite value. */ |
| break; |
| |
| case MPD_TRIPLE_ERROR: |
| /* TypeError check: can be omitted if the type of dec is known. */ |
| if (PyErr_Occurred()) { |
| return NULL; |
| } |
| |
| /* Too large for conversion. PyDec_AsUint128Triple() does not set an |
| exception so applications can choose themselves. Typically this |
| would be a ValueError. */ |
| PyErr_SetString(PyExc_ValueError, |
| "value out of bounds for a uint128 triple"); |
| return NULL; |
| } |
| |
| .. c:function:: PyObject *PyDec_FromUint128Triple(const mpd_uint128_triple_t *triple) |
| |
| Create a decimal from a triple. The following rules must be observed for |
| initializing the triple: |
| |
| 1) ``triple.sign`` must always be 0 (for positive) or 1 (for negative). |
| |
| 2) ``MPD_TRIPLE_QNAN``: ``triple.exp`` must be 0. If ``triple.hi`` or ``triple.lo`` |
| are nonzero, create a ``NaN`` with a payload. |
| |
| 3) ``MPD_TRIPLE_SNAN``: ``triple.exp`` must be 0. If ``triple.hi`` or ``triple.lo`` |
| are nonzero, create an ``sNaN`` with a payload. |
| |
| 4) ``MPD_TRIPLE_INF``: ``triple.exp``, ``triple.hi`` and ``triple.lo`` must be zero. |
| |
| 5) ``MPD_TRIPLE_NORMAL``: ``MPD_MIN_ETINY + 38 < triple.exp < MPD_MAX_EMAX - 38``. |
| ``triple.hi`` and ``triple.lo`` can be chosen freely. |
| |
| 6) ``MPD_TRIPLE_ERROR``: It is always an error to set this tag. |
| |
| |
| If one of the above conditions is not met, the function returns ``NaN`` if |
| the ``InvalidOperation`` trap is not set in the thread local context. Otherwise, |
| it sets the ``InvalidOperation`` exception and returns NULL. |
| |
| Additionally, though extremely unlikely give the small allocation sizes, |
| the function can set ``MemoryError`` and return ``NULL``. |
| |
| |
| Advanced API |
| ------------ |
| |
| This API enables the use of ``libmpdec`` functions. Since Python is compiled with |
| hidden symbols, the API requires an external libmpdec and the ``mpdecimal.h`` |
| header. |
| |
| |
| Functions |
| ~~~~~~~~~ |
| |
| .. c:function:: PyObject *PyDec_Alloc(void) |
| |
| Return a new decimal that can be used in the ``result`` position of ``libmpdec`` |
| functions. |
| |
| .. c:function:: mpd_t *PyDec_Get(PyObject *v) |
| |
| Get a pointer to the internal ``mpd_t`` of the decimal. Decimals are immutable, |
| so this function must only be used on a new Decimal that has been created by |
| PyDec_Alloc(). |
| |
| .. c:function:: const mpd_t *PyDec_GetConst(const PyObject *v) |
| |
| Get a pointer to the constant internal ``mpd_t`` of the decimal. |