Skip to main content

Objects/longobject.c (part 13)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/longobject.c

This annotation covers byte conversion and bit operations. See objects_longobject12_detail for int.__new__, arithmetic, and int.__hash__.

Map

LinesSymbolRole
1-80int.bit_lengthNumber of bits needed to represent
81-160int.bit_countCount of set bits (popcount)
161-260int.to_bytesConvert to bytes in given byte order
261-380int.from_bytesConstruct from bytes
381-500int.as_integer_ratioExact rational representation

Reading

int.bit_length

// CPython: Objects/longobject.c:2040 long_bit_length
static PyObject *
long_bit_length(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Number of bits needed: floor(log2(|n|)) + 1 */
PyLongObject *z = (PyLongObject *)self;
Py_ssize_t ndigits = _PyLong_IsNonNegativeCompact(z) ?
_PyLong_CompactValue(z) : _PyLong_DigitCount(z);
if (ndigits == 0) return PyLong_FromLong(0);
/* Most significant digit */
digit msd = z->long_value.ob_digit[ndigits - 1];
int bits = (ndigits - 1) * PyLong_SHIFT + _Py_bit_length(msd);
return PyLong_FromSsize_t(bits);
}

(255).bit_length() returns 8; (256).bit_length() returns 9. For compact ints (fits in one digit), the computation is a single __builtin_clz equivalent. For large ints, only the most significant digit needs bit-counting.

int.bit_count

// CPython: Objects/longobject.c:2080 long_bit_count
static PyObject *
long_bit_count(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Count of 1 bits (popcount) in |self| */
PyLongObject *z = (PyLongObject *)self;
Py_ssize_t count = 0;
Py_ssize_t ndigits = _PyLong_IsNonNegativeCompact(z) ?
1 : _PyLong_DigitCount(z);
for (Py_ssize_t i = 0; i < ndigits; i++) {
count += _Py_popcount_32(z->long_value.ob_digit[i]);
}
return PyLong_FromSsize_t(count);
}

(7).bit_count() returns 3 (binary 111). Added in Python 3.10. Each 30-bit digit is popcounted separately using __builtin_popcount. Negative numbers use the absolute value.

int.to_bytes

// CPython: Objects/longobject.c:2160 long_to_bytes
static PyObject *
long_to_bytes(PyObject *self, PyObject *args, PyObject *kwargs)
{
Py_ssize_t length;
const char *byteorder_str;
int is_signed = 0;
/* length and byteorder are positional-or-keyword */
PyArg_ParseTupleAndKeywords(args, kwargs, "ns|p:to_bytes",
kwlist, &length, &byteorder_str, &is_signed);
int little_endian = strcmp(byteorder_str, "little") == 0;
PyObject *result = PyBytes_FromStringAndSize(NULL, length);
_PyLong_AsByteArray((PyLongObject *)self, (unsigned char *)PyBytes_AS_STRING(result),
length, little_endian, is_signed);
return result;
}

(1024).to_bytes(2, 'big') returns b'\x04\x00'. _PyLong_AsByteArray fills the output buffer digit by digit. OverflowError is raised if length is too small. signed=True uses two's complement encoding.

int.from_bytes

// CPython: Objects/longobject.c:2240 long_from_bytes
static PyObject *
long_from_bytes(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
Py_buffer bytes_val;
const char *byteorder_str;
int is_signed = 0;
PyArg_ParseTupleAndKeywords(args, kwargs, "y*s|p:from_bytes", ...);
PyObject *result = _PyLong_FromByteArray(
(const unsigned char *)bytes_val.buf, bytes_val.len,
little_endian, is_signed);
PyBuffer_Release(&bytes_val);
return result;
}

int.from_bytes(b'\x04\x00', 'big') returns 1024. Accepts any buffer protocol object. signed=True interprets the leading bit as the sign bit (two's complement). This is the inverse of to_bytes.

gopy notes

int.bit_length is objects.IntBitLength using bits.Len from math/bits. int.bit_count uses bits.OnesCount. int.to_bytes converts to big.Int then calls FillBytes. int.from_bytes uses new(big.Int).SetBytes(...) with endian reversal for little-endian.