|
| 1 | +.. sectionauthor:: Stefan Krah |
| 2 | + |
| 3 | +.. highlight:: c |
| 4 | + |
| 5 | + |
| 6 | +Decimal capsule API |
| 7 | +=================== |
| 8 | + |
| 9 | +Capsule API functions can be used in the same manner as regular library |
| 10 | +functions, provided that the API has been initialized. |
| 11 | + |
| 12 | + |
| 13 | +Initialize |
| 14 | +---------- |
| 15 | + |
| 16 | +Typically, a C extension module that uses the decimal API will do these |
| 17 | +steps in its init function: |
| 18 | + |
| 19 | +.. code-block:: |
| 20 | +
|
| 21 | + #include "pydecimal.h" |
| 22 | +
|
| 23 | + static int decimal_initialized = 0; |
| 24 | + if (!decimal_initialized) { |
| 25 | + if (import_decimal() < 0) { |
| 26 | + return NULL; |
| 27 | + } |
| 28 | +
|
| 29 | + decimal_initialized = 1; |
| 30 | + } |
| 31 | +
|
| 32 | +
|
| 33 | +Type checking, predicates, accessors |
| 34 | +------------------------------------ |
| 35 | + |
| 36 | +.. c:function:: int PyDec_TypeCheck(const PyObject *dec) |
| 37 | +
|
| 38 | + Return 1 if ``dec`` is a Decimal, 0 otherwise. This function does not set |
| 39 | + any exceptions. |
| 40 | +
|
| 41 | +
|
| 42 | +.. c:function:: int PyDec_IsSpecial(const PyObject *dec) |
| 43 | +
|
| 44 | + Return 1 if ``dec`` is ``NaN``, ``sNaN`` or ``Infinity``, 0 otherwise. |
| 45 | +
|
| 46 | + Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| 47 | + this is the only failure mode, so if ``dec`` has already been type-checked, no |
| 48 | + errors can occur and the function can be treated as a simple predicate. |
| 49 | +
|
| 50 | +
|
| 51 | +.. c:function:: int PyDec_IsNaN(const PyObject *dec) |
| 52 | +
|
| 53 | + Return 1 if ``dec`` is ``NaN`` or ``sNaN``, 0 otherwise. |
| 54 | +
|
| 55 | + Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| 56 | + this is the only failure mode, so if ``dec`` has already been type-checked, no |
| 57 | + errors can occur and the function can be treated as a simple predicate. |
| 58 | +
|
| 59 | +
|
| 60 | +.. c:function:: int PyDec_IsInfinite(const PyObject *dec) |
| 61 | +
|
| 62 | + Return 1 if ``dec`` is ``Infinity``, 0 otherwise. |
| 63 | +
|
| 64 | + Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| 65 | + this is the only failure mode, so if ``dec`` has already been type-checked, no |
| 66 | + errors can occur and the function can be treated as a simple predicate. |
| 67 | +
|
| 68 | +
|
| 69 | +.. c:function:: int64_t PyDec_GetDigits(const PyObject *dec) |
| 70 | +
|
| 71 | + Return the number of digits in the coefficient. For ``Infinity``, the |
| 72 | + number of digits is always zero. Typically, the same applies to ``NaN`` |
| 73 | + and ``sNaN``, but both of these can have a payload that is equivalent to |
| 74 | + a coefficient. Therefore, ``NaNs`` can have a nonzero return value. |
| 75 | +
|
| 76 | + Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that |
| 77 | + this is the only failure mode, so if ``dec`` has already been type-checked, no |
| 78 | + errors can occur and the function can be treated as a simple accessor. |
| 79 | +
|
| 80 | +
|
| 81 | +Exact conversions between decimals and primitive C types |
| 82 | +-------------------------------------------------------- |
| 83 | +
|
| 84 | +This API supports conversions for decimals with a coefficient up to 38 digits. |
| 85 | +
|
| 86 | +Data structures |
| 87 | +~~~~~~~~~~~~~~~ |
| 88 | +
|
| 89 | +The conversion functions use the following status codes and data structures: |
| 90 | +
|
| 91 | +.. code-block:: |
| 92 | +
|
| 93 | + /* status cases for getting a triple */ |
| 94 | + enum mpd_triple_class { |
| 95 | + MPD_TRIPLE_NORMAL, |
| 96 | + MPD_TRIPLE_INF, |
| 97 | + MPD_TRIPLE_QNAN, |
| 98 | + MPD_TRIPLE_SNAN, |
| 99 | + MPD_TRIPLE_ERROR, |
| 100 | + }; |
| 101 | +
|
| 102 | + typedef struct { |
| 103 | + enum mpd_triple_class tag; |
| 104 | + uint8_t sign; |
| 105 | + uint64_t hi; |
| 106 | + uint64_t lo; |
| 107 | + int64_t exp; |
| 108 | + } mpd_uint128_triple_t; |
| 109 | +
|
| 110 | +The status cases are explained below. ``sign`` is 0 for positive and 1 for negative. |
| 111 | +``((uint128_t)hi << 64) + lo`` is the coefficient, ``exp`` is the exponent. |
| 112 | +
|
| 113 | +The data structure is called "triple" because the decimal triple (sign, coeff, exp) |
| 114 | +is an established term and (``hi``, ``lo``) represents a single ``uint128_t`` coefficient. |
| 115 | +
|
| 116 | +
|
| 117 | +Functions |
| 118 | +~~~~~~~~~ |
| 119 | +
|
| 120 | +.. c:function:: mpd_uint128_triple_t PyDec_AsUint128Triple(const PyObject *dec) |
| 121 | +
|
| 122 | + Convert a decimal to a triple. As above, it is guaranteed that the only |
| 123 | + Python failure mode is a TypeError, checks can be omitted if the type is |
| 124 | + known. |
| 125 | +
|
| 126 | + For simplicity, the usage of the function and all special cases are |
| 127 | + explained in code form and comments: |
| 128 | +
|
| 129 | +.. code-block:: |
| 130 | +
|
| 131 | + triple = PyDec_AsUint128Triple(dec); |
| 132 | + switch (triple.tag) { |
| 133 | + case MPD_TRIPLE_QNAN: |
| 134 | + /* |
| 135 | + * Success: handle a quiet NaN. |
| 136 | + * 1) triple.sign is 0 or 1. |
| 137 | + * 2) triple.exp is always 0. |
| 138 | + * 3) If triple.hi or triple.lo are nonzero, the NaN has a payload. |
| 139 | + */ |
| 140 | + break; |
| 141 | +
|
| 142 | + case MPD_TRIPLE_SNAN: |
| 143 | + /* |
| 144 | + * Success: handle a signaling NaN. |
| 145 | + * 1) triple.sign is 0 or 1. |
| 146 | + * 2) triple.exp is always 0. |
| 147 | + * 3) If triple.hi or triple.lo are nonzero, the sNaN has a payload. |
| 148 | + */ |
| 149 | + break; |
| 150 | +
|
| 151 | + case MPD_TRIPLE_INF: |
| 152 | + /* |
| 153 | + * Success: handle Infinity. |
| 154 | + * 1) triple.sign is 0 or 1. |
| 155 | + * 2) triple.exp is always 0. |
| 156 | + * 3) triple.hi and triple.lo are always zero. |
| 157 | + */ |
| 158 | + break; |
| 159 | +
|
| 160 | + case MPD_TRIPLE_NORMAL: |
| 161 | + /* Success: handle a finite value. */ |
| 162 | + break; |
| 163 | +
|
| 164 | + case MPD_TRIPLE_ERROR: |
| 165 | + /* TypeError check: can be omitted if the type of dec is known. */ |
| 166 | + if (PyErr_Occurred()) { |
| 167 | + return NULL; |
| 168 | + } |
| 169 | +
|
| 170 | + /* Too large for conversion. PyDec_AsUint128Triple() does not set an |
| 171 | + exception so applications can choose themselves. Typically this |
| 172 | + would be a ValueError. */ |
| 173 | + PyErr_SetString(PyExc_ValueError, |
| 174 | + "value out of bounds for a uint128 triple"); |
| 175 | + return NULL; |
| 176 | + } |
| 177 | +
|
| 178 | +.. c:function:: PyObject *PyDec_FromUint128Triple(const mpd_uint128_triple_t *triple) |
| 179 | +
|
| 180 | + Create a decimal from a triple. The following rules must be observed for |
| 181 | + initializing the triple: |
| 182 | +
|
| 183 | + 1) ``triple.sign`` must always be 0 (for positive) or 1 (for negative). |
| 184 | +
|
| 185 | + 2) ``MPD_TRIPLE_QNAN``: ``triple.exp`` must be 0. If ``triple.hi`` or ``triple.lo`` |
| 186 | + are nonzero, create a ``NaN`` with a payload. |
| 187 | +
|
| 188 | + 3) ``MPD_TRIPLE_SNAN``: ``triple.exp`` must be 0. If ``triple.hi`` or ``triple.lo`` |
| 189 | + are nonzero, create an ``sNaN`` with a payload. |
| 190 | +
|
| 191 | + 4) ``MPD_TRIPLE_INF``: ``triple.exp``, ``triple.hi`` and ``triple.lo`` must be zero. |
| 192 | +
|
| 193 | + 5) ``MPD_TRIPLE_NORMAL``: ``MPD_MIN_ETINY + 38 < triple.exp < MPD_MAX_EMAX - 38``. |
| 194 | + ``triple.hi`` and ``triple.lo`` can be chosen freely. |
| 195 | +
|
| 196 | + 6) ``MPD_TRIPLE_ERROR``: It is always an error to set this tag. |
| 197 | +
|
| 198 | +
|
| 199 | + If one of the above conditions is not met, the function returns ``NaN`` if |
| 200 | + the ``InvalidOperation`` trap is not set in the thread local context. Otherwise, |
| 201 | + it sets the ``InvalidOperation`` exception and returns NULL. |
| 202 | +
|
| 203 | + Additionally, though extremely unlikely give the small allocation sizes, |
| 204 | + the function can set ``MemoryError`` and return ``NULL``. |
| 205 | +
|
| 206 | +
|
| 207 | +Advanced API |
| 208 | +------------ |
| 209 | +
|
| 210 | +This API enables the use of ``libmpdec`` functions. Since Python is compiled with |
| 211 | +hidden symbols, the API requires an external libmpdec and the ``mpdecimal.h`` |
| 212 | +header. |
| 213 | +
|
| 214 | +
|
| 215 | +Functions |
| 216 | +~~~~~~~~~ |
| 217 | +
|
| 218 | +.. c:function:: PyObject *PyDec_Alloc(void) |
| 219 | +
|
| 220 | + Return a new decimal that can be used in the ``result`` position of ``libmpdec`` |
| 221 | + functions. |
| 222 | +
|
| 223 | +.. c:function:: mpd_t *PyDec_Get(PyObject *v) |
| 224 | +
|
| 225 | + Get a pointer to the internal ``mpd_t`` of the decimal. Decimals are immutable, |
| 226 | + so this function must only be used on a new Decimal that has been created by |
| 227 | + PyDec_Alloc(). |
| 228 | +
|
| 229 | +.. c:function:: const mpd_t *PyDec_GetConst(const PyObject *v) |
| 230 | +
|
| 231 | + Get a pointer to the constant internal ``mpd_t`` of the decimal. |
0 commit comments