What does the power operator (**) in Python translate into?
Asked Answered
I

5

18

In other words, what exists behind the two asterisks? Is it simply multiplying the number x times or something else?

As a follow-up question, is it better to write 2**3 or 2*2*2. I'm asking because I've heard that in C++ it's better to not use pow() for simple calculations, since it calls a function.

Intraatomic answered 17/11, 2015 at 16:50 Comment(5)
Write 2**3 rather than 2*2*2. It's more readable.Collar
That doesn't really answer my questionIntraatomic
That's why it's a comment, and not an answer ;)Collar
It seems like an answer to me. 2**3 is better because it's more readable. Did you mean to ask "is it faster to write 2**3"?Cowlick
The issue with C++ pow() isn't that it's a function (most C++ compilers these days can inline simple functions just fine with optimizations enabled even when the function isn't explicitly marked as inline), the issue is that it's designed for floating-point exponentiation, so it's overkill for integers. If you want an integer-only pow, see stackoverflow.com/questions/101439.Vshaped
R
21

If you're interested in the internals, I'd disassemble the instruction to get the CPython bytecode it maps to. Using Python3:

»»» def test():
    return 2**3
   ...: 
»»» dis.dis(test)
  2           0 LOAD_CONST               3 (8)
              3 RETURN_VALUE

OK, so that seems to have done the calculation right on entry, and stored the result. You get exactly the same CPython bytecode for 2*2*2 (feel free to try it). So, for the expressions that evaluate to a constant, you get the same result and it doesn't matter.

What if you want the power of a variable?

Now you get two different bits of bytecode:

»»» def test(n):
        return n ** 3

»»» dis.dis(test)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (3)
              6 BINARY_POWER
              7 RETURN_VALUE

vs.

»»» def test(n):
    return n * 2 * 2
   ....: 

»»» dis.dis(test)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (2)
              6 BINARY_MULTIPLY
              7 LOAD_CONST               1 (2)
             10 BINARY_MULTIPLY
             11 RETURN_VALUE

Now the question is of course, is the BINARY_MULTIPLY quicker than the BINARY_POWER operation?

The best way to try that is to use timeit. I'll use the IPython %timeit magic. Here's the output for multiplication:

%timeit test(100)
The slowest run took 15.52 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 163 ns per loop

and for power

The slowest run took 5.44 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 473 ns per loop

You may wish to repeat this for representative inputs, but empirically it looks like the multiplication is quicker (but note the mentioned caveat about the variance in the output).

If you want further internals, I'd suggest digging into the CPython code.

Redbud answered 17/11, 2015 at 17:4 Comment(5)
I think your test function shouldn't use a return statement, only the operation itself. I'm annoying, I know.Gamesome
I actually tried that first, here are the instructions that the first example translates to without return: LOAD_CONST, POP_TOP, LOAD_CONST, RETURN_VALUE. I'm not sure that adds much to the analysis.Redbud
It is between returning NullObject or int. I'd rather return Null.Gamesome
This answer was a huge help, explaining why I was getting blazingly fast numbers for %timeit 10**13 but much slower for i = 10; %timeit i**13 and couldn't find any optimizations in Python's longobject.c code that could explain it!Sarad
You're comparing 100³ to 4*100 so your answer doesn't rally make sense. I advise readers to see other answers as they rightly compare the operators.Also as mentioned in another answer the power operator is actually an advantage over multiplications when exponents get big not especially when the base grows.Shetler
W
7

While the second one is little bit faster for numbers, the advantage is very low compared to the first: readability. If you are going for time, and you are pressured to make such optimizations, then python probably is not the language you should use.

Note: for values other than numbers:

a ** b translates to

a.__pow__(b) 

whereas a * a * a is a call to

a.__mul__(a.__mul__(a))

Test Code:

import time

s = time.time()
for x in xrange(1,1000000):
    x**5
print "done in ", time.time() - s

s = time.time()
for x in xrange(1,1000000):
    x*x*x*x*x
print "done in ", time.time() - s

For my machine it yields:

done in  0.975429058075
done in  0.260419845581
[Finished in 1.2s]
Wyly answered 17/11, 2015 at 17:12 Comment(0)
C
3

If you ask frankly, multiplication is a bit faster.

>>timeit.timeit('[i*i*i*i for i in range(100)]', number=10000)
0.262529843304
>>timeit.timeit('[i**4 for i in range(100)]', number=10000)
0.31143438383

But, speed isn't the only thing to consider when you will choose one from two options. From example, what is easier while computing 2 to the power 20? Simply writing 2**20 or using for loop that will iterate 20 times and do some multiplication task?

Calculable answered 17/11, 2015 at 17:15 Comment(0)
O
1

The ** operator will, internally, use an iterative function (same semantics as built-in pow() (Python docs), which likely means it just calls that function anyway).

Therefore, if you know the power and can hardcode it, using 2*2*2 would likely be a little faster than 2**3. This has a little to do with the function, but I believe the main performance issue is that it will use a loop.


Note that it's still quite silly to replace more readable code for less readable code when it's something as simple as 2**3, the performance gain is minimal at best.

Officialism answered 17/11, 2015 at 17:3 Comment(1)
Your closing comment is key here. Supposing that 2*2*2 is marginally faster than 2**3, it should be stressed that if you find yourself needing to employ such micro-optimizations in your Python code, then Python was probably the wrong choice of language.Collar
J
1

From the docs:

The power operator binds more tightly than unary operators on its left; it binds less tightly than unary operators on its right. The syntax is:

power ::=  primary ["**" u_expr]

Thus, in an unparenthesized sequence of power and unary operators, the operators are evaluated from right to left (this does not constrain the evaluation order for the operands): -1**2 results in -1.

The power operator has the same semantics as the built-in pow() function, when called with two arguments: it yields its left argument raised to the power of its right argument.

This means that, in Python: 2**2**3 is evaluated as 2**(2**3) = 2**8 = 256.

In mathematics, stacked exponents are applied from the top down. If it were not done this way you would just get multiplication of exponents:

(((2**3)**4)**5) = 2**(3*4*5)

It might be a little faster just to do the multiplication, but much less readable.

Jourdain answered 17/11, 2015 at 17:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.