Index: Objects/intobject.c =================================================================== --- Objects/intobject.c (revision 67815) +++ Objects/intobject.c (working copy) @@ -1138,6 +1138,37 @@ return NULL; } +static PyObject * +int_bit_length(PyIntObject *v) +{ + unsigned long n; + long r = 0; + + if (v->ob_ival < 0) + n = (unsigned long)((-1 - v->ob_ival) + 1); + else + n = (unsigned long)v->ob_ival; + + while (n >= 128) { + r += 8; + n >>= 8; + } + while (n != 0) { + r += 1; + n >>= 1; + } + return PyInt_FromLong(r); +} + +PyDoc_STRVAR(int_bit_length_doc, +"int.bit_length() -> int\n\ +\n\ +Return number of bits necessary to represent n in binary.\n\ +>>> bin(37)\n\ +'0b100101'\n\ +>>> (37).bit_length()\n\ +6"); + #if 0 static PyObject * int_is_finite(PyObject *v) @@ -1149,6 +1180,8 @@ 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: Objects/longobject.c =================================================================== --- Objects/longobject.c (revision 67815) +++ Objects/longobject.c (working copy) @@ -3451,6 +3451,73 @@ return PyInt_FromSsize_t(res); } +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 >= 128) { + msd_bits += 8; + msd >>= 8; + } + while (msd != 0) { + msd_bits += 1; + msd >>= 1; + } + + 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\ +Return number of bits necessary to represent n in binary.\n\ +>>> bin(37L)\n\ +'0b100101'\n\ +>>> (37L).bit_length()\n\ +6"); + #if 0 static PyObject * long_is_finite(PyObject *v) @@ -3462,6 +3529,8 @@ 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."}, Index: Doc/whatsnew/2.7.rst =================================================================== --- Doc/whatsnew/2.7.rst (revision 67815) +++ Doc/whatsnew/2.7.rst (working copy) @@ -66,8 +66,24 @@ 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. + + .. ====================================================================== Index: Doc/library/stdtypes.rst =================================================================== --- Doc/library/stdtypes.rst (revision 67815) +++ Doc/library/stdtypes.rst (working copy) @@ -447,6 +447,39 @@ 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(37) + '0b100101' + >>> n.bit_length() + 6 + >>> n = -0b00011010 + >>> n.bit_length() + 5 + + More precisely, if ``x`` is nonzero then ``x.bit_length()`` + returns the unique positive integer ``k`` such that ``2**(k-1) <= + abs(x) < 2**k``. ``(0).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 --------------------------- Index: Lib/test/test_int.py =================================================================== --- Lib/test/test_int.py (revision 67815) +++ Lib/test/test_int.py (working copy) @@ -2,6 +2,7 @@ import unittest from test.test_support import run_unittest, have_unicode +import math L = [ ('0', 0), @@ -240,6 +241,40 @@ 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 + assert k == len(bin(x).lstrip('-0b') + # Behaviour as specified in the docs + if x != 0: + assert 2**(k-1) <= abs(x) < 2**k + else: + assert k == 0 + assert k == len(bin(x).lstrip('-0b')) + # 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. + assert 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: Lib/test/test_long.py =================================================================== --- Lib/test/test_long.py (revision 67815) +++ Lib/test/test_long.py (working copy) @@ -3,6 +3,7 @@ import sys import random +import math # Used for lazy formatting of failure messages class Frm(object): @@ -752,6 +753,41 @@ 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 + assert k == len(bin(x).lstrip('-0b')) + # Behaviour as specified in the docs + if x != 0: + assert 2**(k-1) <= abs(x) < 2**k + else: + assert 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. + assert 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)