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:
#include "pydecimal.h"
static int decimal_initialized = 0;
if (!decimal_initialized) {
if (import_decimal() < 0) {
return NULL;
}
decimal_initialized = 1;
}
Type checking, predicates, accessors¶
-
int
PyDec_TypeCheck
(const PyObject *dec)¶ Return 1 if
dec
is a Decimal, 0 otherwise. This function does not set any exceptions.
-
int
PyDec_IsSpecial
(const PyObject *dec)¶ Return 1 if
dec
isNaN
,sNaN
orInfinity
, 0 otherwise.Set TypeError and return -1 if
dec
is not a Decimal. It is guaranteed that this is the only failure mode, so ifdec
has already been type-checked, no errors can occur and the function can be treated as a simple predicate.
-
int
PyDec_IsNaN
(const PyObject *dec)¶ Return 1 if
dec
isNaN
orsNaN
, 0 otherwise.Set TypeError and return -1 if
dec
is not a Decimal. It is guaranteed that this is the only failure mode, so ifdec
has already been type-checked, no errors can occur and the function can be treated as a simple predicate.
-
int
PyDec_IsInfinite
(const PyObject *dec)¶ Return 1 if
dec
isInfinity
, 0 otherwise.Set TypeError and return -1 if
dec
is not a Decimal. It is guaranteed that this is the only failure mode, so ifdec
has already been type-checked, no errors can occur and the function can be treated as a simple predicate.
-
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 toNaN
andsNaN
, 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 ifdec
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:
/* 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¶
-
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:
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;
}
-
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:
triple.sign
must always be 0 (for positive) or 1 (for negative).MPD_TRIPLE_QNAN
:triple.exp
must be 0. Iftriple.hi
ortriple.lo
are nonzero, create aNaN
with a payload.MPD_TRIPLE_SNAN
:triple.exp
must be 0. Iftriple.hi
ortriple.lo
are nonzero, create ansNaN
with a payload.MPD_TRIPLE_INF
:triple.exp
,triple.hi
andtriple.lo
must be zero.MPD_TRIPLE_NORMAL
:MPD_MIN_ETINY + 38 < triple.exp < MPD_MAX_EMAX - 38
.triple.hi
andtriple.lo
can be chosen freely.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 theInvalidOperation
trap is not set in the thread local context. Otherwise, it sets theInvalidOperation
exception and returns NULL.Additionally, though extremely unlikely give the small allocation sizes, the function can set
MemoryError
and returnNULL
.
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¶
-
PyObject *
PyDec_Alloc
(void)¶ Return a new decimal that can be used in the
result
position oflibmpdec
functions.