Objects/longobject.c (part 11)
Source:
cpython 3.14 @ ab2d84fe1023/Objects/longobject.c
This annotation covers bit manipulation and byte serialization. See objects_longobject10_detail for Karatsuba multiplication, _PyLong_Divrem, and pow().
Map
| Lines | Symbol | Role |
|---|---|---|
| 1-80 | int.bit_length | Count significant bits excluding sign and leading zeros |
| 81-160 | int.bit_count | Population count (number of set bits) |
| 161-280 | int.to_bytes | Serialize to big/little-endian bytes |
| 281-420 | int.from_bytes | Deserialize from bytes with optional signed interpretation |
| 421-600 | PyLong_AsNativeBytes | C API: extract to a fixed-width buffer |
Reading
int.bit_length
// CPython: Objects/longobject.c:3820 long_bit_length_impl
static PyObject *
long_bit_length_impl(PyObject *self)
{
/* The number of bits = (ndigits - 1) * PyLong_SHIFT + bit_length(top_digit) */
Py_ssize_t ndigits = _PyLong_DigitCount((PyLongObject *)self);
if (ndigits == 0) return PyLong_FromLong(0);
digit msd = _PyLong_GetDigit((PyLongObject *)self, ndigits - 1);
int bits = _Py_bit_length(msd);
return PyLong_FromSsize_t((ndigits - 1) * PyLong_SHIFT + bits);
}
(2**100).bit_length() returns 101. _Py_bit_length uses __builtin_clz on GCC/Clang, falling back to a lookup table. PyLong_SHIFT is 30 on 64-bit platforms (each digit stores 30 bits).
int.bit_count
// CPython: Objects/longobject.c:3870 long_bit_count_impl
static PyObject *
long_bit_count_impl(PyObject *self)
{
/* Sum the popcount of each 30-bit digit.
For negative ints: two's complement infinite precision —
bit_count is defined only for non-negative ints. */
if (_PyLong_IsNegative((PyLongObject *)self)) {
PyErr_SetString(PyExc_ValueError, "...");
return NULL;
}
Py_ssize_t count = 0;
Py_ssize_t ndigits = _PyLong_DigitCount((PyLongObject *)self);
for (Py_ssize_t i = 0; i < ndigits; i++) {
count += _Py_popcount32(_PyLong_GetDigit((PyLongObject *)self, i));
}
return PyLong_FromSsize_t(count);
}
int.bit_count() (added in Python 3.10) uses __builtin_popcount per digit. (255).bit_count() returns 8; (256).bit_count() returns 1.
int.to_bytes
// CPython: Objects/longobject.c:3920 long_to_bytes_impl
static PyObject *
long_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder,
int is_signed)
{
/* Allocate 'length' bytes; fill from self's digits.
Check for overflow if is_signed=False and self >= 0. */
PyObject *result = PyBytes_FromStringAndSize(NULL, length);
char *buf = PyBytes_AS_STRING(result);
memset(buf, 0, length);
/* Unpack digits into buf, little-endian first */
_PyLong_AsByteArray((PyLongObject *)self, (unsigned char *)buf,
length, /*little_endian=*/1, is_signed, /*with_exceptions=*/1);
if (byteorder_is_big)
_Py_ReverseBytes((unsigned char *)buf, length);
return result;
}
(1024).to_bytes(4, 'big') returns b'\x00\x00\x04\x00'. Raises OverflowError if the value doesn't fit in length bytes. signed=True uses two's complement for negative values.
int.from_bytes
// CPython: Objects/longobject.c:4020 long_from_bytes_impl
static PyObject *
long_from_bytes_impl(PyObject *type, PyObject *bytes_obj,
PyObject *byteorder, int is_signed)
{
Py_buffer view;
PyObject_GetBuffer(bytes_obj, &view, PyBUF_SIMPLE);
if (byteorder_is_big)
_Py_ReverseBytes((unsigned char *)tmp_buf, view.len);
PyObject *result = _PyLong_FromByteArray((unsigned char *)view.buf,
view.len, /*little_endian=*/1, is_signed);
PyBuffer_Release(&view);
return result;
}
int.from_bytes(b'\xff\xff', signed=True) returns -1; int.from_bytes(b'\xff\xff', signed=False) returns 65535. The function is a classmethod so it works with int subclasses.
PyLong_AsNativeBytes
// CPython: Objects/longobject.c:4160 PyLong_AsNativeBytes
Py_ssize_t
PyLong_AsNativeBytes(PyObject *v, void *buffer, Py_ssize_t n, int flags)
{
/* New in 3.13. Replaces _PyLong_AsByteArray.
Returns the number of bytes written, or the minimum buffer size needed.
flags: Py_ASNATIVEBYTES_NATIVE_ENDIAN, _BIG_ENDIAN, _LITTLE_ENDIAN,
_UNSIGNED_BUFFER, _REJECT_NEGATIVE, _ALLOW_INDEX */
...
}
PyLong_AsNativeBytes is the preferred C API for interfacing int with fixed-width C types. Pass n=0 to query the required size without writing. The flags argument replaces the old is_signed/little_endian boolean pair.
gopy notes
int.bit_length is objects.LongBitLength in objects/long.go using bits.Len. int.bit_count uses bits.OnesCount. int.to_bytes calls objects.LongToBytes which uses big.Int.Bytes() plus endian swap. int.from_bytes uses big.Int.SetBytes. PyLong_AsNativeBytes is objects.LongAsNativeBytes.