Skip to content

Commit b5c4363

Browse files
author
mark.dickinson
committed
Issue #3439: add bit_length method to int and long.
Thanks Fredrik Johansson and Victor Stinner for code, Raymond Hettinger for review. git-svn-id: http://svn.python.org/projects/python/trunk@67822 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 25e5a9f commit b5c4363

8 files changed

Lines changed: 239 additions & 1 deletion

File tree

Doc/library/stdtypes.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,41 @@ Notes:
447447
A right shift by *n* bits is equivalent to division by ``pow(2, n)``.
448448

449449

450+
Additional Methods on Integer Types
451+
-----------------------------------
452+
453+
.. method:: int.bit_length()
454+
.. method:: long.bit_length()
455+
456+
For any integer ``x``, ``x.bit_length()`` returns the number of
457+
bits necessary to represent ``x`` in binary, excluding the sign
458+
and any leading zeros::
459+
460+
>>> n = 37
461+
>>> bin(n)
462+
'0b100101'
463+
>>> n.bit_length()
464+
6
465+
>>> n = -0b00011010
466+
>>> n.bit_length()
467+
5
468+
469+
More precisely, if ``x`` is nonzero then ``x.bit_length()`` is the
470+
unique positive integer ``k`` such that ``2**(k-1) <= abs(x) <
471+
2**k``. Equivalently, ``x.bit_length()`` is equal to ``1 +
472+
floor(log(x, 2))`` [#]_ . If ``x`` is zero then ``x.bit_length()``
473+
gives ``0``.
474+
475+
Equivalent to::
476+
477+
def bit_length(self):
478+
'Number of bits necessary to represent self in binary.'
479+
return len(bin(self).lstrip('-0b'))
480+
481+
482+
.. versionadded:: 2.7
483+
484+
450485
Additional Methods on Float
451486
---------------------------
452487

@@ -2648,6 +2683,11 @@ types, where they are relevant. Some of these are not reported by the
26482683
.. [#] As a consequence, the list ``[1, 2]`` is considered equal to ``[1.0, 2.0]``, and
26492684
similarly for tuples.
26502685
2686+
.. [#] Beware of this formula! It's mathematically valid, but as a
2687+
Python expression it will not give correct results for all ``x``,
2688+
as a consequence of the limited precision of floating-point
2689+
arithmetic.
2690+
26512691
.. [#] They must have since the parser can't tell the type of the operands.
26522692
26532693
.. [#] To format only a tuple you should therefore provide a singleton tuple whose only

Doc/whatsnew/2.7.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,23 @@ Other Language Changes
6666

6767
Some smaller changes made to the core Python language are:
6868

69-
* List of changes to be written here.
69+
* The :func:`int` and :func:`long` types gained a ``bit_length``
70+
method that returns the number of bits necessary to represent
71+
its argument in binary::
72+
73+
>>> n = 37
74+
>>> bin(37)
75+
'0b100101'
76+
>>> n.bit_length()
77+
6
78+
>>> n = 2**123-1
79+
>>> n.bit_length()
80+
123
81+
>>> (n+1).bit_length()
82+
124
83+
84+
(Contributed by Fredrik Johansson and Victor Stinner; :issue:`3439`.)
85+
7086

7187
.. ======================================================================
7288

Lib/test/test_int.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import unittest
44
from test.test_support import run_unittest, have_unicode
5+
import math
56

67
L = [
78
('0', 0),
@@ -240,6 +241,40 @@ def test_basic(self):
240241
self.assertEqual(int('2br45qc', 35), 4294967297L)
241242
self.assertEqual(int('1z141z5', 36), 4294967297L)
242243

244+
def test_bit_length(self):
245+
tiny = 1e-10
246+
for x in xrange(-65000, 65000):
247+
k = x.bit_length()
248+
# Check equivalence with Python version
249+
self.assertEqual(k, len(bin(x).lstrip('-0b')))
250+
# Behaviour as specified in the docs
251+
if x != 0:
252+
self.assert_(2**(k-1) <= abs(x) < 2**k)
253+
else:
254+
self.assertEqual(k, 0)
255+
# Alternative definition: x.bit_length() == 1 + floor(log_2(x))
256+
if x != 0:
257+
# When x is an exact power of 2, numeric errors can
258+
# cause floor(log(x)/log(2)) to be one too small; for
259+
# small x this can be fixed by adding a small quantity
260+
# to the quotient before taking the floor.
261+
self.assertEqual(k, 1 + math.floor(
262+
math.log(abs(x))/math.log(2) + tiny))
263+
264+
self.assertEqual((0).bit_length(), 0)
265+
self.assertEqual((1).bit_length(), 1)
266+
self.assertEqual((-1).bit_length(), 1)
267+
self.assertEqual((2).bit_length(), 2)
268+
self.assertEqual((-2).bit_length(), 2)
269+
for i in [2, 3, 15, 16, 17, 31, 32, 33, 63, 64]:
270+
a = 2**i
271+
self.assertEqual((a-1).bit_length(), i)
272+
self.assertEqual((1-a).bit_length(), i)
273+
self.assertEqual((a).bit_length(), i+1)
274+
self.assertEqual((-a).bit_length(), i+1)
275+
self.assertEqual((a+1).bit_length(), i+1)
276+
self.assertEqual((-a-1).bit_length(), i+1)
277+
243278
def test_intconversion(self):
244279
# Test __int__()
245280
class ClassicMissingMethods:

Lib/test/test_long.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44

55
import random
6+
import math
67

78
# Used for lazy formatting of failure messages
89
class Frm(object):
@@ -752,6 +753,42 @@ def test_nan_inf(self):
752753
self.assertRaises(OverflowError, long, float('-inf'))
753754
self.assertRaises(ValueError, long, float('nan'))
754755

756+
def test_bit_length(self):
757+
tiny = 1e-10
758+
for x in xrange(-65000, 65000):
759+
x = long(x)
760+
k = x.bit_length()
761+
# Check equivalence with Python version
762+
self.assertEqual(k, len(bin(x).lstrip('-0b')))
763+
# Behaviour as specified in the docs
764+
if x != 0:
765+
self.assert_(2**(k-1) <= abs(x) < 2**k)
766+
else:
767+
self.assertEqual(k, 0)
768+
# Alternative definition: x.bit_length() == 1 + floor(log_2(x))
769+
if x != 0:
770+
# When x is an exact power of 2, numeric errors can
771+
# cause floor(log(x)/log(2)) to be one too small; for
772+
# small x this can be fixed by adding a small quantity
773+
# to the quotient before taking the floor.
774+
self.assertEqual(k, 1 + math.floor(
775+
math.log(abs(x))/math.log(2) + tiny))
776+
777+
self.assertEqual((0L).bit_length(), 0)
778+
self.assertEqual((1L).bit_length(), 1)
779+
self.assertEqual((-1L).bit_length(), 1)
780+
self.assertEqual((2L).bit_length(), 2)
781+
self.assertEqual((-2L).bit_length(), 2)
782+
for i in [2, 3, 15, 16, 17, 31, 32, 33, 63, 64, 234]:
783+
a = 2L**i
784+
self.assertEqual((a-1).bit_length(), i)
785+
self.assertEqual((1-a).bit_length(), i)
786+
self.assertEqual((a).bit_length(), i+1)
787+
self.assertEqual((-a).bit_length(), i+1)
788+
self.assertEqual((a+1).bit_length(), i+1)
789+
self.assertEqual((-a-1).bit_length(), i+1)
790+
791+
755792
def test_main():
756793
test_support.run_unittest(LongTest)
757794

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ Drew Jenkins
343343
Flemming Kj�r Jensen
344344
Jiba
345345
Orjan Johansen
346+
Fredrik Johansson
346347
Gregory K. Johnson
347348
Simon Johnston
348349
Evan Jones

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ What's New in Python 2.7 alpha 1
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #3439: Add a bit_length method to int and long.
16+
1517
- Issue #2183: Simplify and optimize bytecode for list comprehensions.
1618
Original patch by Neal Norwitz.
1719

Objects/intobject.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,40 @@ int__format__(PyObject *self, PyObject *args)
11381138
return NULL;
11391139
}
11401140

1141+
static const unsigned char BitLengthTable[32] = {
1142+
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
1143+
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
1144+
};
1145+
1146+
static PyObject *
1147+
int_bit_length(PyIntObject *v)
1148+
{
1149+
unsigned long n;
1150+
long r = 0;
1151+
1152+
if (v->ob_ival < 0)
1153+
/* avoid undefined behaviour when v->ob_ival == -LONG_MAX-1 */
1154+
n = 0U-(unsigned long)v->ob_ival;
1155+
else
1156+
n = (unsigned long)v->ob_ival;
1157+
1158+
while (n >= 32) {
1159+
r += 6;
1160+
n >>= 6;
1161+
}
1162+
r += (long)(BitLengthTable[n]);
1163+
return PyInt_FromLong(r);
1164+
}
1165+
1166+
PyDoc_STRVAR(int_bit_length_doc,
1167+
"int.bit_length() -> int\n\
1168+
\n\
1169+
Number of bits necessary to represent self in binary.\n\
1170+
>>> bin(37)\n\
1171+
'0b100101'\n\
1172+
>>> (37).bit_length()\n\
1173+
6");
1174+
11411175
#if 0
11421176
static PyObject *
11431177
int_is_finite(PyObject *v)
@@ -1149,6 +1183,8 @@ int_is_finite(PyObject *v)
11491183
static PyMethodDef int_methods[] = {
11501184
{"conjugate", (PyCFunction)int_int, METH_NOARGS,
11511185
"Returns self, the complex conjugate of any int."},
1186+
{"bit_length", (PyCFunction)int_bit_length, METH_NOARGS,
1187+
int_bit_length_doc},
11521188
#if 0
11531189
{"is_finite", (PyCFunction)int_is_finite, METH_NOARGS,
11541190
"Returns always True."},

Objects/longobject.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3451,6 +3451,75 @@ long_sizeof(PyLongObject *v)
34513451
return PyInt_FromSsize_t(res);
34523452
}
34533453

3454+
static const unsigned char BitLengthTable[32] = {
3455+
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
3456+
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
3457+
};
3458+
3459+
static PyObject *
3460+
long_bit_length(PyLongObject *v)
3461+
{
3462+
PyLongObject *result, *x, *y;
3463+
Py_ssize_t ndigits, msd_bits = 0;
3464+
digit msd;
3465+
3466+
assert(v != NULL);
3467+
assert(PyLong_Check(v));
3468+
3469+
ndigits = ABS(Py_SIZE(v));
3470+
if (ndigits == 0)
3471+
return PyInt_FromLong(0);
3472+
3473+
msd = v->ob_digit[ndigits-1];
3474+
while (msd >= 32) {
3475+
msd_bits += 6;
3476+
msd >>= 6;
3477+
}
3478+
msd_bits += (long)(BitLengthTable[msd]);
3479+
3480+
if (ndigits <= PY_SSIZE_T_MAX/PyLong_SHIFT)
3481+
return PyInt_FromSsize_t((ndigits-1)*PyLong_SHIFT + msd_bits);
3482+
3483+
/* expression above may overflow; use Python integers instead */
3484+
result = (PyLongObject *)PyLong_FromSsize_t(ndigits - 1);
3485+
if (result == NULL)
3486+
return NULL;
3487+
x = (PyLongObject *)PyLong_FromLong(PyLong_SHIFT);
3488+
if (x == NULL)
3489+
goto error;
3490+
y = (PyLongObject *)long_mul(result, x);
3491+
Py_DECREF(x);
3492+
if (y == NULL)
3493+
goto error;
3494+
Py_DECREF(result);
3495+
result = y;
3496+
3497+
x = (PyLongObject *)PyLong_FromLong(msd_bits);
3498+
if (x == NULL)
3499+
goto error;
3500+
y = (PyLongObject *)long_add(result, x);
3501+
Py_DECREF(x);
3502+
if (y == NULL)
3503+
goto error;
3504+
Py_DECREF(result);
3505+
result = y;
3506+
3507+
return (PyObject *)result;
3508+
3509+
error:
3510+
Py_DECREF(result);
3511+
return NULL;
3512+
}
3513+
3514+
PyDoc_STRVAR(long_bit_length_doc,
3515+
"long.bit_length() -> int or long\n\
3516+
\n\
3517+
Number of bits necessary to represent self in binary.\n\
3518+
>>> bin(37L)\n\
3519+
'0b100101'\n\
3520+
>>> (37L).bit_length()\n\
3521+
6");
3522+
34543523
#if 0
34553524
static PyObject *
34563525
long_is_finite(PyObject *v)
@@ -3462,6 +3531,8 @@ long_is_finite(PyObject *v)
34623531
static PyMethodDef long_methods[] = {
34633532
{"conjugate", (PyCFunction)long_long, METH_NOARGS,
34643533
"Returns self, the complex conjugate of any long."},
3534+
{"bit_length", (PyCFunction)long_bit_length, METH_NOARGS,
3535+
long_bit_length_doc},
34653536
#if 0
34663537
{"is_finite", (PyCFunction)long_is_finite, METH_NOARGS,
34673538
"Returns always True."},

0 commit comments

Comments
 (0)