Skip to main content

Objects/longobject.c (part 8)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/longobject.c

This annotation covers the bit-manipulation and byte-conversion methods. See objects_longobject7_detail for division, __mod__, __pow__, and gcd.

Map

LinesSymbolRole
1-80int.bit_lengthNumber of bits required to represent abs(n)
81-160int.bit_countNumber of 1-bits in abs(n) (population count)
161-300int.to_bytesConvert integer to a bytes object
301-440int.from_bytesClass method: interpret bytes as an integer
441-600int.as_integer_ratioReturn (n, 1) for exact rational representation

Reading

int.bit_length

// CPython: Objects/longobject.c:5880 long_bit_length
static PyObject *
long_bit_length(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Number of bits in abs(self), ignoring leading zeros. */
PyLongObject *a = (PyLongObject *)self;
Py_ssize_t ndigits = _PyLong_DigitCount(a);
if (ndigits == 0) return PyLong_FromLong(0);
/* Most-significant digit contributes its bit length */
digit msd = a->long_value.ob_digit[ndigits - 1];
return PyLong_FromSsize_t((ndigits - 1) * PyLong_SHIFT +
_Py_bit_length(msd));
}

(1024).bit_length() returns 11 (2^10 = 1024). Used by math.log2 and cryptographic code.

int.bit_count

// CPython: Objects/longobject.c:5940 long_bit_count
static PyObject *
long_bit_count(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Count 1-bits in abs(self). Uses popcount per digit. */
PyLongObject *a = (PyLongObject *)self;
Py_ssize_t ndigits = _PyLong_DigitCount(a);
Py_ssize_t count = 0;
for (Py_ssize_t i = 0; i < ndigits; i++) {
count += _Py_popcount32(a->long_value.ob_digit[i]);
}
return PyLong_FromSsize_t(count);
}

(7).bit_count() returns 3 (binary 111). Added in Python 3.10. On platforms with hardware popcount, _Py_popcount32 compiles to a single POPCNT instruction.

int.to_bytes

// CPython: Objects/longobject.c:5980 long_to_bytes
static PyObject *
long_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames)
{
/* to_bytes(length, byteorder, *, signed=False) */
Py_ssize_t length = PyLong_AsSsize_t(args[0]);
const char *byteorder = PyUnicode_AsUTF8(args[1]);
int is_signed = 0; /* from kwnames */
/* Check value fits in length bytes */
int overflow;
_PyLong_AsByteArray((PyLongObject *)self,
(unsigned char *)PyBytes_AS_STRING(result),
length, little_endian, is_signed, &overflow);
...
return result;
}

(1024).to_bytes(2, 'big') returns b'\x04\x00'. _PyLong_AsByteArray traverses the digit array in the correct byte order, handling sign extension for signed conversion.

int.from_bytes

// CPython: Objects/longobject.c:6060 long_from_bytes
static PyObject *
long_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames)
{
/* from_bytes(bytes, byteorder, *, signed=False) — class method */
Py_buffer buf;
PyObject_GetBuffer(args[0], &buf, PyBUF_SIMPLE);
const char *byteorder = PyUnicode_AsUTF8(args[1]);
PyObject *result = _PyLong_FromByteArray(
(const unsigned char *)buf.buf, buf.len,
little_endian, is_signed);
PyBuffer_Release(&buf);
return result;
}

int.from_bytes(b'\x04\x00', 'big') returns 1024. Works with any bytes-like object via the buffer protocol.

int.as_integer_ratio

// CPython: Objects/longobject.c:6140 long_as_integer_ratio
static PyObject *
long_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Always (n, 1) for integers — exact rational representation. */
return PyTuple_Pack(2, self, _PyLong_GetOne());
}

(42).as_integer_ratio() returns (42, 1). This satisfies the numbers.Rational interface, allowing integers to be used wherever a rational number is expected.

gopy notes

int.bit_length is objects.LongBitLength in objects/long_bitop.go. int.bit_count uses bits.OnesCount from Go's math/bits. int.to_bytes/from_bytes use big-endian or little-endian byte slices. int.as_integer_ratio returns a tuple (self, One).