1

I'm interested in creating decimal.Decimal objects in an extension in C. I cannot find any documentation about this in the docs

Does documentation for this exist? Are there any examples anywhere?

I've found the source code that implements the object in the Python git repository so I'm sure I could do some trial and error, and muddle through working out the header file imports, but documentation is preferred (if it exists).

martineau
  • 119,623
  • 25
  • 170
  • 301
Bryant
  • 3,011
  • 1
  • 18
  • 26
  • 1
    You need to use one of the `PyDecType_From***` functions. Depending on from what other object you want to create a decimal object. Or `PyDecType_New` if you want to create one from scratch. – mkrieger1 Feb 23 '21 at 00:53
  • I don't think this is a public interface, so you can probably use it but you won't find documentation. – DavidW Feb 23 '21 at 07:35
  • I'd use the functions, but they don't seem to be exported in any header file :( – Bryant Feb 23 '21 at 18:03

2 Answers2

3

In Python 3.10, there's going to be a C API for this. You'll need to supply your own libmpdec and mpdecimal.h header, and initialize the decimal API with import_decimal(). (Note that the C-level import_decimal() call is completely different from and unrelated to the Python-level statement import decimal.)

After setup, you can allocate an uninitialized Decimal object with

PyObject *dec = PyDec_Alloc();

and then get a pointer to the internal mpd_t with

mpd_t *dec_internal = PyDec_Get(dec);

and initialize the mpd_t with libmpdec functions. (Note that decimal objects are logically immutable, so you should refrain from mutating any Decimal object other than one you have just allocated with PyDec_Alloc.)

To work with the mpd_t of an existing Decimal object, you can do

const mpd_t *dec_internal = PyDec_GetConst(some_existing_decimal);

and then use non-mutative libmpdec functions.


As for pre-3.10 code, there is no Decimal C API. You'll just have to do the C-level equivalent of import decimal; d = Decimal(whatever) using functions like PyImport_ImportModule, PyObject_GetAttrString, and PyObject_Call.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    I've based this answer on the Python 3.10.0a6 alpha docs, but I don't think the details of this API have been fully worked out yet - it's likely the answer will need revision once Python 3.10 is actually released. – user2357112 Mar 04 '21 at 14:56
  • looking forward to migrating from 3.9 to 3.10 - thanks! – Bryant Mar 04 '21 at 18:48
2

Your question was about the decimal.Decimal class, whereas the linked code points to the optimized module _decimal which uses the library libmpdec.

The following snippet will work for Python 3.9 for all three cases (decimal/_decimal/_pydecimal).

PyObject *module = PyImport_ImportModule((char *) "decimal");
PyObject *clazz = PyObject_GetAttrString(module, (char *) "Decimal");
PyObject *value = PyUnicode_FromString("123.45");
PyObject *object = PyObject_CallOneArg(clazz, value);
PyObject  *str = PyObject_Str(object);
// C++
std::cout << "dec: " << PyUnicode_AsUTF8(str) << std::endl;
// C
// printf("dec: %s\n", PyUnicode_AsUTF8(str));

output:

dec: 123.45

some background informatin for the _decimal implementation in Python 3.3 can be found at https://bugs.python.org/issue7652

SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • They're writing C, not C++. – user2357112 Mar 04 '21 at 14:36
  • @user2357112supportsMonica You are right. Except for the output, it should not change much the documentation https://docs.python.org/3.9/c-api/index.html says `This manual documents the API used by C and C++ programmers who want to write extension modules or embed Python.`. – SubOptimal Mar 04 '21 at 14:42