39

What is the simplest function to return the smallest power of 2 that is greater than or equal to a given non-negative integer in Python?

For example, the smallest power of 2 greater than or equal to 6 is 8.

Daira Hopwood
  • 2,264
  • 22
  • 14
jhoyla
  • 1,191
  • 1
  • 9
  • 26

6 Answers6

54

Let's test it:

import collections
import math
import timeit

def power_bit_length(x):
    return 2**(x-1).bit_length()

def shift_bit_length(x):
    return 1<<(x-1).bit_length()

def power_log(x):
    return 2**(math.ceil(math.log(x, 2)))

def test(f):
    collections.deque((f(i) for i in range(1, 1000001)), maxlen=0)

def timetest(f):
    print('{}: {}'.format(timeit.timeit(lambda: test(f), number=10),
                          f.__name__))

timetest(power_bit_length)
timetest(shift_bit_length)
timetest(power_log)

The reason I'm using range(1, 1000001) instead of just range(1000000) is that the power_log version will fail on 0. The reason I'm using a small number of reps over a largeish range instead of lots of reps over a small range is because I expect that different versions will have different performance over different domains. (If you expect to be calling this with huge thousand-bit numbers, of course, you want a test that uses those.)

With Apple Python 2.7.2:

4.38817000389: power_bit_length
3.69475698471: shift_bit_length
7.91623902321: power_log

With Python.org Python 3.3.0:

6.566169916652143: power_bit_length
3.098236607853323: shift_bit_length
9.982460380066186: power_log

With pypy 1.9.0/2.7.2:

2.8580930233: power_bit_length
2.49524712563: shift_bit_length
3.4371240139: power_log

I believe this demonstrates that the 2** is the slow part here; using bit_length instead of log does speed things up, but using 1<< instead of 2** is more important.

Also, I think it's clearer. The OP's version requires you to make a mental context-switch from logarithms to bits, and then back to exponents. Either stay in bits the whole time (shift_bit_length), or stay in logs and exponents (power_log).

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 5
    Please note that the result is **incorrect** for `x == 0`, since `(-1).bit_length() == 1` in Python. – Siu Ching Pong -Asuka Kenji- Mar 16 '15 at 18:35
  • Take care about accuracy. `math.log(2**29,2)` is 29.000000000000004 so `power_log(2**29)` gives an incorrect answer of 30. – Colonel Panic May 02 '17 at 13:02
  • 3
    @ColonelPanic The problem you noted is non-existent if `math.log2` is used, as it rightfully should be. The problem exists starting at 29 only if `math.log` is used. – Asclepius Feb 11 '18 at 21:36
  • On Windows with Python 3.10.8, using 10,000 random numbers, the relative times are a bit different: 0.3 for shift_bit_length, 0.7 for power_bit_length, 1.0 for power_log – mins Dec 27 '22 at 13:47
22

Always returning 2**(x - 1).bit_length() is incorrect because although it returns 1 for x=1, it returns a non-monotonic 2 for x=0. A simple fix that is monotonically safe for x=0 is:

def next_power_of_2(x):  
    return 1 if x == 0 else 2**(x - 1).bit_length()

Sample outputs:

>>> print(', '.join(f'{x}:{next_power_of_2(x)}' for x in range(10)))
0:1, 1:1, 2:2, 3:4, 4:4, 5:8, 6:8, 7:8, 8:8, 9:16

It can pedantically be argued that x=0 should return 0 (and not 1), since 2**float('-inf') == 0.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
jhoyla
  • 1,191
  • 1
  • 9
  • 26
  • 1
    Isn't that awfully slow for large `x`? Apart from that, I can't say I understand it. –  Jan 10 '13 at 21:28
  • @delnan -- Why would you expect this to be slow? (not that I understand the code either ...) – mgilson Jan 10 '13 at 21:33
  • @delnan: First, `bit_length` is effectively log base 2 rounded up - 1, and very quickly. So, raise 2 to the power of that, and you're done. Maybe doing `1 <<` instead of `2 **` would be faster, but otherwise, what slowness are you expecting here? – abarnert Jan 10 '13 at 21:34
  • I'm not sure how python implements bit_length, but it's pretty much instant even for huge values of `x`. – jhoyla Jan 10 '13 at 21:35
  • bit_length returns "Number of bits necessary to represent self in binary." – jhoyla Jan 10 '13 at 21:36
  • 2
    Nevermind, I read this as taking 2 to the `x-1`th power, then taking the `bit_length` of that. It's actually the other way around. With that, the intermediate integer would get quite large quickly, but this way it's more reasonable. Still not what I'd call intuitive. –  Jan 10 '13 at 21:37
  • @delnan: Once you make the switch in your head from powers of 2 to bits, it makes sense. It might be better to make that more explicit somehow (although I'm not exactly sure how, other than a comment. Any suggestions?). Or, if the 2:1 speedup (even smaller in Py3 and PyPy) isn't relevant, maybe the `math.log` version is better, but see the comments there. – abarnert Jan 10 '13 at 21:50
18

Would this work for you:

import math

def next_power_of_2(x):
    return 1 if x == 0 else 2**math.ceil(math.log2(x))

Note that math.log2 is available in Python 3 but not in Python 2. Using it instead of math.log avoids numerical problems with the latter at 2**29 and beyond.

Sample outputs:

>>> print(', '.join(f'{x}:{next_power_of_2(x)}' for x in range(10)))
0:1, 1:1, 2:2, 3:4, 4:4, 5:8, 6:8, 7:8, 8:8, 9:16

It can pedantically be argued that x=0 should return 0 (and not 1), since 2**float('-inf') == 0.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
  • Requires log, which I think is slower. – jhoyla Jan 10 '13 at 21:35
  • @jhoyla Performance is very rarely relevant (and the slow part would be looking up two functions and calling them, not `log` specifically). This is definitely more readable and obvious (for me at least). –  Jan 10 '13 at 21:40
  • The only way to find out if it's slower is to test… but it does have the disadvantage that it says `next_power_of_two(0)` is a `DomainError` instead of 1… – abarnert Jan 10 '13 at 21:40
  • 1
    @jhoyla: Oh, good point. There's an easy fix for each version, but I'm not sure which one looks clearer once fixed… (Also, it's arguable that `next_power_of_two(0)` should be `0`, not `1`, because `0` is the `-inf`th power, and therefore also the `-inf+1`th… But either way, `2` is clearly wrong.) – abarnert Jan 10 '13 at 21:50
4

Hmm, I know this question is old and my answer is pretty simple, but I am really surprised in all this time nobody posted it here, so I will post it as an answer.

The easiest solution is really simple, no need to import any library, you can do it in one loop if you use while statement!

So the logic is very simple, create a variable with a value of 1, while the value of the variable is less than the number, multiply the variable by 2!

Code:

i = 1
while i < n: i *=2

The return values for n = 1, 63, 64, 255, 256, 1000, 4095:

2, 64, 64, 256, 256, 1024, 4096

This can be easily modified to calculate the next power closet to a number of other bases as well:

def nextpower(num, base):
  i = 1
  while i < num: i *= base
  return i
3

We can do this as follows using bit manipulation:

def next_power_of_2(n):
    if n == 0:
        return 1
    if n & (n - 1) == 0:
        return n
    while n & (n - 1) > 0:
        n &= (n - 1)
    return n << 1

Sample outputs:

>>> print(', '.join(f'{x}:{next_power_of_2(x)}' for x in range(10)))
0:1, 1:1, 2:2, 3:4, 4:4, 5:8, 6:8, 7:8, 8:8, 9:16

For further reading, refer to this resource.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
Akash Kandpal
  • 3,126
  • 28
  • 25
0
v+=(v==0);
v--;
v|=v>>1;
v|=v>>2;
v|=v>>4;
v|=v>>8;
v|=v>>16;
v++;

For a 16-bit integer.

sudeepdino008
  • 3,194
  • 5
  • 39
  • 73