summaryrefslogtreecommitdiffstats
path: root/Objects/longobject.c
diff options
context:
space:
mode:
authorNiklas Fiekas <niklas.fiekas@backscattering.de>2020-05-29 16:28:02 (GMT)
committerGitHub <noreply@github.com>2020-05-29 16:28:02 (GMT)
commit8bd216dfede9cb2d5bedb67f20a30c99844dbfb8 (patch)
tree0cae604e76e929467bba3cb1ebbec3ac8a417947 /Objects/longobject.c
parent364b5ead1584583db91ef7f9d9f87f01bfbb5774 (diff)
downloadcpython-8bd216dfede9cb2d5bedb67f20a30c99844dbfb8.zip
cpython-8bd216dfede9cb2d5bedb67f20a30c99844dbfb8.tar.gz
cpython-8bd216dfede9cb2d5bedb67f20a30c99844dbfb8.tar.bz2
bpo-29882: Add an efficient popcount method for integers (#771)
* bpo-29882: Add an efficient popcount method for integers * Update 'sign bit' and versionadded in docs * Add entry to whatsnew document * Doc: use positive example, mention population count * Minor cleanups of the core code * Move popcount_digit closer to where it's used * Use z instead of self after conversion * Add 'absolute value' and 'population count' to docstring * Fix clinic error about missing summary line * Ensure popcount_digit is portable with 64-bit ints Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
Diffstat (limited to 'Objects/longobject.c')
-rw-r--r--Objects/longobject.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 4ae17c9..0b209a4 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -5304,6 +5304,75 @@ int_bit_length_impl(PyObject *self)
return NULL;
}
+static int
+popcount_digit(digit d)
+{
+ /* 32bit SWAR popcount. */
+ uint32_t u = d;
+ u -= (u >> 1) & 0x55555555U;
+ u = (u & 0x33333333U) + ((u >> 2) & 0x33333333U);
+ u = (u + (u >> 4)) & 0x0f0f0f0fU;
+ return (uint32_t)(u * 0x01010101U) >> 24;
+}
+
+/*[clinic input]
+int.bit_count
+
+Number of ones in the binary representation of the absolute value of self.
+
+Also known as the population count.
+
+>>> bin(13)
+'0b1101'
+>>> (13).bit_count()
+3
+[clinic start generated code]*/
+
+static PyObject *
+int_bit_count_impl(PyObject *self)
+/*[clinic end generated code: output=2e571970daf1e5c3 input=7e0adef8e8ccdf2e]*/
+{
+ assert(self != NULL);
+ assert(PyLong_Check(self));
+
+ PyLongObject *z = (PyLongObject *)self;
+ Py_ssize_t ndigits = Py_ABS(Py_SIZE(z));
+ Py_ssize_t bit_count = 0;
+
+ /* Each digit has up to PyLong_SHIFT ones, so the accumulated bit count
+ from the first PY_SSIZE_T_MAX/PyLong_SHIFT digits can't overflow a
+ Py_ssize_t. */
+ Py_ssize_t ndigits_fast = Py_MIN(ndigits, PY_SSIZE_T_MAX/PyLong_SHIFT);
+ for (Py_ssize_t i = 0; i < ndigits_fast; i++) {
+ bit_count += popcount_digit(z->ob_digit[i]);
+ }
+
+ PyObject *result = PyLong_FromSsize_t(bit_count);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ /* Use Python integers if bit_count would overflow. */
+ for (Py_ssize_t i = ndigits_fast; i < ndigits; i++) {
+ PyObject *x = PyLong_FromLong(popcount_digit(z->ob_digit[i]));
+ if (x == NULL) {
+ goto error;
+ }
+ PyObject *y = long_add((PyLongObject *)result, (PyLongObject *)x);
+ Py_DECREF(x);
+ if (y == NULL) {
+ goto error;
+ }
+ Py_DECREF(result);
+ result = y;
+ }
+
+ return result;
+
+ error:
+ Py_DECREF(result);
+ return NULL;
+}
/*[clinic input]
int.as_integer_ratio
@@ -5460,6 +5529,7 @@ static PyMethodDef long_methods[] = {
{"conjugate", long_long_meth, METH_NOARGS,
"Returns self, the complex conjugate of any int."},
INT_BIT_LENGTH_METHODDEF
+ INT_BIT_COUNT_METHODDEF
INT_TO_BYTES_METHODDEF
INT_FROM_BYTES_METHODDEF
INT_AS_INTEGER_RATIO_METHODDEF