расширяя python с помощью c, как справиться с целыми числами произвольного размера? - PullRequest
4 голосов
/ 03 мая 2019

В руководстве по API Python / C упоминаются функции преобразования из пустых указателей и в пустые указатели, которые, по-видимому, являются единственным способом использования целых чисел Python произвольной длины в C.
(1): PyLong_FromVoidPtr() и формат 0& с Py_BuildValue()
(2): PyLong_AsVoidPtr() и форматы 0, 0& и 0! с PyArg_…Parse…()

Однако я не нашел в руководстве никаких указаний на то, как использовать эти указатели void, чтобы делать что-либо в C с этими произвольными длинными целыми числами.
(3): я попытался найти «voidptr», «void *» и «0 &», но еще не полностью прочитал все.

Где я могу найти информацию об их внутренней структуре или примитивах для их вычисления?

Ответы [ 2 ]

5 голосов
/ 03 мая 2019

На самом деле эти функции должны иметь не «указатель на произвольно большое целое число», а буквально просто целочисленные значения как указатель void *, как, например, приведенный к типу void *.См. Реализации для PyLong_FromVoidPtr и PyLong_AsVoidPtr.Это просто для того, чтобы вы могли держать произвольные указатели в Python, чтобы убедиться, что приведение выполнено правильно.

Насколько я могу судить, наиболее практичный способ получить произвольные длинные целые числа из и в Python был бы с int.to_bytes и int.from_bytes.На самом деле есть API-интерфейс для внутреннего использования _PyLong_FromByteArray / _PyLong_AsByteArray, который вы, вероятно, можете использовать.См. Связанный с этим вопрос Расширение Python - эффективно создавайте и проверяйте большие целые числа .

Примечание. Интересно, что, похоже, не существует какого-либо C API, официального или иного, чтобы сообщить бит или байтдлина целочисленного значения Python.В Python есть int.bit_length, но он не отображается ни в какую общедоступную функцию.

3 голосов
/ 03 мая 2019

Документация есть в Include/longintrepr.h:

/* Parameters of the integer representation.  There are two different
   sets of parameters: one set for 30-bit digits, stored in an unsigned 32-bit
   integer type, and one set for 15-bit digits with each digit stored in an
   unsigned short.  The value of PYLONG_BITS_IN_DIGIT, defined either at
   configure time or in pyport.h, is used to decide which digit size to use.

   Type 'digit' should be able to hold 2*PyLong_BASE-1, and type 'twodigits'
   should be an unsigned integer type able to hold all integers up to
   PyLong_BASE*PyLong_BASE-1.  x_sub assumes that 'digit' is an unsigned type,
   and that overflow is handled by taking the result modulo 2**N for some N >
   PyLong_SHIFT.  The majority of the code doesn't care about the precise
   value of PyLong_SHIFT, but there are some notable exceptions:

   - long_pow() requires that PyLong_SHIFT be divisible by 5

   - PyLong_{As,From}ByteArray require that PyLong_SHIFT be at least 8

   - long_hash() requires that PyLong_SHIFT is *strictly* less than the number
     of bits in an unsigned long, as do the PyLong <-> long (or unsigned long)
     conversion functions

   - the Python int <-> size_t/Py_ssize_t conversion functions expect that
     PyLong_SHIFT is strictly less than the number of bits in a size_t

   - the marshal code currently expects that PyLong_SHIFT is a multiple of 15

   - NSMALLNEGINTS and NSMALLPOSINTS should be small enough to fit in a single
     digit; with the current values this forces PyLong_SHIFT >= 9

  The values 15 and 30 should fit all of the above requirements, on any
  platform.
*/

Длина int - это длина части переменной длины, умноженной на 15/16 в битах - цифры либо 30биты в uint32_t, #if PYLONG_BITS_IN_DIGIT == 30, иначе 15 бит в uint16_t;структура длинного объекта:

struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
};

Существует элемент ob_size , который сообщит размер в байтах - поэтому, если PYLONG_BITS_IN_DIGIT равен 30, ob_digit является массивомob_size / sizeof(uint32_t) uint32_t с, 30 битов значимы в каждом;в противном случае ob_digit - это массив ob_size / sizeof(uint16_t) uint16_t с, в каждой цифре хранится 15 значащих битов.

Это все часть Include/longintrepr.h, но они раскрываются только #ifndef Py_LIMITED_API!

...