Issue #3439: add bit_length method to int and long.

Thanks Fredrik Johansson and Victor Stinner for code,
Raymond Hettinger for review.
This commit is contained in:
Mark Dickinson 2008-12-17 16:14:37 +00:00
parent d0c3515bc5
commit 1a707981c8
8 changed files with 239 additions and 1 deletions

View File

@ -447,6 +447,41 @@ Notes:
A right shift by *n* bits is equivalent to division by ``pow(2, n)``. 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 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 .. [#] As a consequence, the list ``[1, 2]`` is considered equal to ``[1.0, 2.0]``, and
similarly for tuples. 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. .. [#] 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 .. [#] To format only a tuple you should therefore provide a singleton tuple whose only

View File

@ -66,7 +66,23 @@ Other Language Changes
Some smaller changes made to the core Python language are: 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`.)
.. ====================================================================== .. ======================================================================

View File

@ -2,6 +2,7 @@ import sys
import unittest import unittest
from test.test_support import run_unittest, have_unicode from test.test_support import run_unittest, have_unicode
import math
L = [ L = [
('0', 0), ('0', 0),
@ -240,6 +241,40 @@ class IntTestCases(unittest.TestCase):
self.assertEqual(int('2br45qc', 35), 4294967297L) self.assertEqual(int('2br45qc', 35), 4294967297L)
self.assertEqual(int('1z141z5', 36), 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): def test_intconversion(self):
# Test __int__() # Test __int__()
class ClassicMissingMethods: class ClassicMissingMethods:

View File

@ -3,6 +3,7 @@ from test import test_support
import sys import sys
import random import random
import math
# Used for lazy formatting of failure messages # Used for lazy formatting of failure messages
class Frm(object): class Frm(object):
@ -752,6 +753,42 @@ class LongTest(unittest.TestCase):
self.assertRaises(OverflowError, long, float('-inf')) self.assertRaises(OverflowError, long, float('-inf'))
self.assertRaises(ValueError, long, float('nan')) 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(): def test_main():
test_support.run_unittest(LongTest) test_support.run_unittest(LongTest)

View File

@ -343,6 +343,7 @@ Drew Jenkins
Flemming Kjær Jensen Flemming Kjær Jensen
Jiba Jiba
Orjan Johansen Orjan Johansen
Fredrik Johansson
Gregory K. Johnson Gregory K. Johnson
Simon Johnston Simon Johnston
Evan Jones Evan Jones

View File

@ -12,6 +12,8 @@ What's New in Python 2.7 alpha 1
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #3439: Add a bit_length method to int and long.
- Issue #2183: Simplify and optimize bytecode for list comprehensions. - Issue #2183: Simplify and optimize bytecode for list comprehensions.
Original patch by Neal Norwitz. Original patch by Neal Norwitz.

View File

@ -1138,6 +1138,40 @@ int__format__(PyObject *self, PyObject *args)
return NULL; 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 #if 0
static PyObject * static PyObject *
int_is_finite(PyObject *v) int_is_finite(PyObject *v)
@ -1149,6 +1183,8 @@ int_is_finite(PyObject *v)
static PyMethodDef int_methods[] = { static PyMethodDef int_methods[] = {
{"conjugate", (PyCFunction)int_int, METH_NOARGS, {"conjugate", (PyCFunction)int_int, METH_NOARGS,
"Returns self, the complex conjugate of any int."}, "Returns self, the complex conjugate of any int."},
{"bit_length", (PyCFunction)int_bit_length, METH_NOARGS,
int_bit_length_doc},
#if 0 #if 0
{"is_finite", (PyCFunction)int_is_finite, METH_NOARGS, {"is_finite", (PyCFunction)int_is_finite, METH_NOARGS,
"Returns always True."}, "Returns always True."},

View File

@ -3451,6 +3451,75 @@ long_sizeof(PyLongObject *v)
return PyInt_FromSsize_t(res); 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 #if 0
static PyObject * static PyObject *
long_is_finite(PyObject *v) long_is_finite(PyObject *v)
@ -3462,6 +3531,8 @@ long_is_finite(PyObject *v)
static PyMethodDef long_methods[] = { static PyMethodDef long_methods[] = {
{"conjugate", (PyCFunction)long_long, METH_NOARGS, {"conjugate", (PyCFunction)long_long, METH_NOARGS,
"Returns self, the complex conjugate of any long."}, "Returns self, the complex conjugate of any long."},
{"bit_length", (PyCFunction)long_bit_length, METH_NOARGS,
long_bit_length_doc},
#if 0 #if 0
{"is_finite", (PyCFunction)long_is_finite, METH_NOARGS, {"is_finite", (PyCFunction)long_is_finite, METH_NOARGS,
"Returns always True."}, "Returns always True."},