]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #3439: add bit_length method to int and long.
authorMark Dickinson <dickinsm@gmail.com>
Wed, 17 Dec 2008 16:14:37 +0000 (16:14 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Wed, 17 Dec 2008 16:14:37 +0000 (16:14 +0000)
Thanks Fredrik Johansson and Victor Stinner for code,
Raymond Hettinger for review.

Doc/library/stdtypes.rst
Doc/whatsnew/2.7.rst
Lib/test/test_int.py
Lib/test/test_long.py
Misc/ACKS
Misc/NEWS
Objects/intobject.c
Objects/longobject.c

index 00d420bda9a0300f0829030d27cad869dfdb9cdf..be684ed6c9b361a051f3e1fbf1ae5f145f5e613f 100644 (file)
@@ -447,6 +447,41 @@ Notes:
    A right shift by *n* bits is equivalent to division by ``pow(2, n)``.
 
 
+Additional Methods on Integer Types
+-----------------------------------
+
+.. method:: int.bit_length()
+.. method:: long.bit_length()
+
+    For any integer ``x``, ``x.bit_length()`` returns the number of
+    bits necessary to represent ``x`` in binary, excluding the sign
+    and any leading zeros::
+
+        >>> n = 37
+        >>> bin(n)
+        '0b100101'
+        >>> n.bit_length()
+        6
+        >>> n = -0b00011010
+        >>> n.bit_length()
+        5
+
+    More precisely, if ``x`` is nonzero then ``x.bit_length()`` is the
+    unique positive integer ``k`` such that ``2**(k-1) <= abs(x) <
+    2**k``.  Equivalently, ``x.bit_length()`` is equal to ``1 +
+    floor(log(x, 2))`` [#]_ . If ``x`` is zero then ``x.bit_length()``
+    gives ``0``.
+
+    Equivalent to::
+
+        def bit_length(self):
+            'Number of bits necessary to represent self in binary.'
+            return len(bin(self).lstrip('-0b'))
+
+
+    .. versionadded:: 2.7
+
+
 Additional Methods on Float
 ---------------------------
 
@@ -2648,6 +2683,11 @@ types, where they are relevant.  Some of these are not reported by the
 .. [#] As a consequence, the list ``[1, 2]`` is considered equal to ``[1.0, 2.0]``, and
    similarly for tuples.
 
+.. [#] Beware of this formula!  It's mathematically valid, but as a
+   Python expression it will not give correct results for all ``x``,
+   as a consequence of the limited precision of floating-point
+   arithmetic.
+
 .. [#] They must have since the parser can't tell the type of the operands.
 
 .. [#] To format only a tuple you should therefore provide a singleton tuple whose only
index a9eb0ba5e4100b78b004099dcc14c2435633ae88..61084c8812e3ab900d0ea7f82466972fa4ceaa22 100644 (file)
@@ -66,7 +66,23 @@ Other Language Changes
 
 Some smaller changes made to the core Python language are:
 
-* List of changes to be written here.
+* The :func:`int` and :func:`long` types gained a ``bit_length``
+  method that returns the number of bits necessary to represent
+  its argument in binary::
+
+      >>> n = 37
+      >>> bin(37)
+      '0b100101'
+      >>> n.bit_length()
+      6
+      >>> n = 2**123-1
+      >>> n.bit_length()
+      123
+      >>> (n+1).bit_length()
+      124
+
+  (Contributed by Fredrik Johansson and Victor Stinner; :issue:`3439`.)
+
 
 .. ======================================================================
 
index a2dc85f68a43b974902cdd72f6f23712a3253d1f..ce18ad2bdeab2059dd79b0214113a3caeee81d7f 100644 (file)
@@ -2,6 +2,7 @@ import sys
 
 import unittest
 from test.test_support import run_unittest, have_unicode
+import math
 
 L = [
         ('0', 0),
@@ -240,6 +241,40 @@ class IntTestCases(unittest.TestCase):
         self.assertEqual(int('2br45qc', 35), 4294967297L)
         self.assertEqual(int('1z141z5', 36), 4294967297L)
 
+    def test_bit_length(self):
+        tiny = 1e-10
+        for x in xrange(-65000, 65000):
+            k = x.bit_length()
+            # Check equivalence with Python version
+            self.assertEqual(k, len(bin(x).lstrip('-0b')))
+            # Behaviour as specified in the docs
+            if x != 0:
+                self.assert_(2**(k-1) <= abs(x) < 2**k)
+            else:
+                self.assertEqual(k, 0)
+            # Alternative definition: x.bit_length() == 1 + floor(log_2(x))
+            if x != 0:
+                # When x is an exact power of 2, numeric errors can
+                # cause floor(log(x)/log(2)) to be one too small; for
+                # small x this can be fixed by adding a small quantity
+                # to the quotient before taking the floor.
+                self.assertEqual(k, 1 + math.floor(
+                        math.log(abs(x))/math.log(2) + tiny))
+
+        self.assertEqual((0).bit_length(), 0)
+        self.assertEqual((1).bit_length(), 1)
+        self.assertEqual((-1).bit_length(), 1)
+        self.assertEqual((2).bit_length(), 2)
+        self.assertEqual((-2).bit_length(), 2)
+        for i in [2, 3, 15, 16, 17, 31, 32, 33, 63, 64]:
+            a = 2**i
+            self.assertEqual((a-1).bit_length(), i)
+            self.assertEqual((1-a).bit_length(), i)
+            self.assertEqual((a).bit_length(), i+1)
+            self.assertEqual((-a).bit_length(), i+1)
+            self.assertEqual((a+1).bit_length(), i+1)
+            self.assertEqual((-a-1).bit_length(), i+1)
+
     def test_intconversion(self):
         # Test __int__()
         class ClassicMissingMethods:
index 5addd2e87584d004ec6e65eec6fb165bf0794a66..e648c0fa1606c2d7471fa66ca649b28e25270bf9 100644 (file)
@@ -3,6 +3,7 @@ from test import test_support
 import sys
 
 import random
+import math
 
 # Used for lazy formatting of failure messages
 class Frm(object):
@@ -752,6 +753,42 @@ class LongTest(unittest.TestCase):
         self.assertRaises(OverflowError, long, float('-inf'))
         self.assertRaises(ValueError, long, float('nan'))
 
+    def test_bit_length(self):
+        tiny = 1e-10
+        for x in xrange(-65000, 65000):
+            x = long(x)
+            k = x.bit_length()
+            # Check equivalence with Python version
+            self.assertEqual(k, len(bin(x).lstrip('-0b')))
+            # Behaviour as specified in the docs
+            if x != 0:
+                self.assert_(2**(k-1) <= abs(x) < 2**k)
+            else:
+                self.assertEqual(k, 0)
+            # Alternative definition: x.bit_length() == 1 + floor(log_2(x))
+            if x != 0:
+                # When x is an exact power of 2, numeric errors can
+                # cause floor(log(x)/log(2)) to be one too small; for
+                # small x this can be fixed by adding a small quantity
+                # to the quotient before taking the floor.
+                self.assertEqual(k, 1 + math.floor(
+                        math.log(abs(x))/math.log(2) + tiny))
+
+        self.assertEqual((0L).bit_length(), 0)
+        self.assertEqual((1L).bit_length(), 1)
+        self.assertEqual((-1L).bit_length(), 1)
+        self.assertEqual((2L).bit_length(), 2)
+        self.assertEqual((-2L).bit_length(), 2)
+        for i in [2, 3, 15, 16, 17, 31, 32, 33, 63, 64, 234]:
+            a = 2L**i
+            self.assertEqual((a-1).bit_length(), i)
+            self.assertEqual((1-a).bit_length(), i)
+            self.assertEqual((a).bit_length(), i+1)
+            self.assertEqual((-a).bit_length(), i+1)
+            self.assertEqual((a+1).bit_length(), i+1)
+            self.assertEqual((-a-1).bit_length(), i+1)
+
+
 def test_main():
     test_support.run_unittest(LongTest)
 
index 72b18c9c5145ec314a582228206291a8e10ab0f6..26e5f1217adca2ef6d12958c3bc88d31cff1a016 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -343,6 +343,7 @@ Drew Jenkins
 Flemming Kjær Jensen
 Jiba
 Orjan Johansen
+Fredrik Johansson
 Gregory K. Johnson
 Simon Johnston
 Evan Jones
index 9e499ae5af93d78df9af73b1b923ac7be28488ec..09c3ceb17a4cc8df26c7ebd7bb6d5c8b66e593d2 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,8 @@ What's New in Python 2.7 alpha 1
 Core and Builtins
 -----------------
 
+- Issue #3439: Add a bit_length method to int and long.
+
 - Issue #2183: Simplify and optimize bytecode for list comprehensions.
   Original patch by Neal Norwitz.
 
index e73c92138c18a280fb39057f8cafbc5495c1e8c4..9baee8e0f73ef8cc0c23d11deb2e3528812e2552 100644 (file)
@@ -1138,6 +1138,40 @@ int__format__(PyObject *self, PyObject *args)
        return NULL;
 }
 
+static const unsigned char BitLengthTable[32] = {
+       0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+};
+
+static PyObject *
+int_bit_length(PyIntObject *v)
+{
+       unsigned long n;
+       long r = 0;
+
+       if (v->ob_ival < 0)
+               /* avoid undefined behaviour when v->ob_ival == -LONG_MAX-1 */
+               n = 0U-(unsigned long)v->ob_ival;
+       else
+               n = (unsigned long)v->ob_ival;
+
+       while (n >= 32) {
+               r += 6;
+               n >>= 6;
+       }
+       r += (long)(BitLengthTable[n]);
+       return PyInt_FromLong(r);
+}
+
+PyDoc_STRVAR(int_bit_length_doc,
+"int.bit_length() -> int\n\
+\n\
+Number of bits necessary to represent self in binary.\n\
+>>> bin(37)\n\
+'0b100101'\n\
+>>> (37).bit_length()\n\
+6");
+
 #if 0
 static PyObject *
 int_is_finite(PyObject *v)
@@ -1149,6 +1183,8 @@ int_is_finite(PyObject *v)
 static PyMethodDef int_methods[] = {
        {"conjugate",   (PyCFunction)int_int,   METH_NOARGS,
         "Returns self, the complex conjugate of any int."},
+       {"bit_length", (PyCFunction)int_bit_length, METH_NOARGS,
+        int_bit_length_doc},
 #if 0
        {"is_finite",   (PyCFunction)int_is_finite,     METH_NOARGS,
         "Returns always True."},
index 65130696d406e4635e1a2527d23ba0ff857b3b7b..ad5557be0aa2914722ef85c6fb6b31ca22a3c3db 100644 (file)
@@ -3451,6 +3451,75 @@ long_sizeof(PyLongObject *v)
        return PyInt_FromSsize_t(res);
 }
 
+static const unsigned char BitLengthTable[32] = {
+       0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+};
+
+static PyObject *
+long_bit_length(PyLongObject *v)
+{
+       PyLongObject *result, *x, *y;
+       Py_ssize_t ndigits, msd_bits = 0;
+       digit msd;
+
+       assert(v != NULL);
+       assert(PyLong_Check(v));
+
+       ndigits = ABS(Py_SIZE(v));
+       if (ndigits == 0)
+               return PyInt_FromLong(0);
+
+       msd = v->ob_digit[ndigits-1];
+       while (msd >= 32) {
+               msd_bits += 6;
+               msd >>= 6;
+       }
+       msd_bits += (long)(BitLengthTable[msd]);
+
+       if (ndigits <= PY_SSIZE_T_MAX/PyLong_SHIFT)
+               return PyInt_FromSsize_t((ndigits-1)*PyLong_SHIFT + msd_bits);
+
+       /* expression above may overflow; use Python integers instead */
+       result = (PyLongObject *)PyLong_FromSsize_t(ndigits - 1);
+       if (result == NULL)
+               return NULL;
+       x = (PyLongObject *)PyLong_FromLong(PyLong_SHIFT);
+       if (x == NULL)
+               goto error;
+       y = (PyLongObject *)long_mul(result, x);
+       Py_DECREF(x);
+       if (y == NULL)
+               goto error;
+       Py_DECREF(result);
+       result = y;
+
+       x = (PyLongObject *)PyLong_FromLong(msd_bits);
+       if (x == NULL)
+               goto error;
+       y = (PyLongObject *)long_add(result, x);
+       Py_DECREF(x);
+       if (y == NULL)
+               goto error;
+       Py_DECREF(result);
+       result = y;
+
+       return (PyObject *)result;
+
+error:
+       Py_DECREF(result);
+       return NULL;
+}
+
+PyDoc_STRVAR(long_bit_length_doc,
+"long.bit_length() -> int or long\n\
+\n\
+Number of bits necessary to represent self in binary.\n\
+>>> bin(37L)\n\
+'0b100101'\n\
+>>> (37L).bit_length()\n\
+6");
+
 #if 0
 static PyObject *
 long_is_finite(PyObject *v)
@@ -3462,6 +3531,8 @@ long_is_finite(PyObject *v)
 static PyMethodDef long_methods[] = {
        {"conjugate",   (PyCFunction)long_long, METH_NOARGS,
         "Returns self, the complex conjugate of any long."},
+       {"bit_length",  (PyCFunction)long_bit_length, METH_NOARGS,
+        long_bit_length_doc},
 #if 0
        {"is_finite",   (PyCFunction)long_is_finite,    METH_NOARGS,
         "Returns always True."},