Exponentiation is one of the most common tasks in programming. In Python, there are several ways to perform exponentiation, each with its own specific features. In this article, we will explore different methods of exponentiation in Python.

## What is Exponentiation?

Exponentiation indicates how many times a number is multiplied by itself.

Example:

[ 2^3 = 2 \times 2 \times 2 = 8 ]

There are three methods to perform exponentiation:

- Easy
- Medium
- Advanced

Let’s take a closer look at each:

## Easy Level

You can use the `**`

operator to raise a number to a power.

```
num = 3 ** 4 # Same as 3 * 3 * 3 * 3
print(num) # 81
```

The `**`

operator is a simple and versatile way that many programmers prefer for exponentiation.

## Medium Level

**Using the pow() Function**

The `pow()`

function is used for exponentiation. It is equivalent to the `**`

operator.

```
number, exponent = 10, 3
print(pow(number, exponent)) # 1000
```

### Using `math.pow()`

The `math.pow()`

function from the `math`

module is also used for exponentiation, but it takes float arguments and returns a float result. Unlike the `pow()`

function, it does not support the modulus quotient.

```
import math
number, exponent = 10, 3
print(math.pow(number, exponent)) # 1000.0
```

### Differences Between `pow()`

and `math.pow()`

While they seem to perform the same task, there are subtle differences:

**pow()**can take three arguments: base, exponent, and modulus.

```
r1 = pow(5, 3) # 5*5*5 = 125
r2 = pow(5, 3, 2) # Equivalent to (5*5*5) % 2
print(r1, r2) # 125 1
```

**math.pow()**uses limited precision.

```
import math
number, exponent, remainder = 13, 100, 2
print(math.pow(number, exponent) % remainder) # 0.0
print(pow(number, exponent, remainder)) # 1
```

**math.pow()**always returns a float.

```
import math
print(math.pow(2, 6)) # 64.0
print(pow(2, 6)) # 64
```

However, if the result is a non-integer, both methods will return a float.

```
import math
print(math.pow(2.1, 6.2)) # 99.48546343115241
print(pow(2.1, 6.2)) # 99.48546343115241
```

**math.pow()**can crash with large numbers.

```
import math
print(pow(2, 9999)) # Will compute without errors
print(math.pow(2, 9999)) # OverflowError: math range error
```

### Potential Differences in Speed

Let’s write code to measure execution speed.

**pow():**

```
import timeit
pow1 = """
pow(2, 100)
"""
elapsed_time1 = timeit.timeit(pow1, number=100_000)
print(elapsed_time1)
```

**math.pow():**

```
from timeit import timeit
pow2 = """
import math
math.pow(2, 100)
"""
elapsed_time2 = timeit(pow2, number=100_000)
print(elapsed_time2)
```

**Results:**

```
print("pow():", elapsed_time1)
print("math.pow():", elapsed_time2)
```

This shows that `pow()`

is faster. However, remember that much depends on the device and platform where the tests are being conducted. Some report that `math.pow()`

is much faster, even with `setup="import math"`

specified.

On a different computer and platform, results vary:

```
pow(): 0.013429596998321358
math.pow(): 0.008748997999646235
```

Here, `math.pow()`

turned out to be one and a half times faster.

## Advanced Level

**Using Numpy**

Numpy is a module that provides common mathematical and numerical operations.

Exponentiation is also a mathematical operation supported by this module.

```
import numpy
print(numpy.power(2, 30)) # 1073741824
```

It is common practice to write `import numpy as np`

, making it easier to write `np.power(2, 30)`

.

If we create a sequence of values with `numpy.arange()`

, for example, some tricks open up for us. Consider the following:

```
import numpy as np
arr1 = np.arange(8)
exponent = 2
print(arr1) # [0 1 2 3 4 5 6 7]
out = np.power(arr1, exponent)
print(out) # [ 0 1 4 9 16 25 36 49]
```

With `arr1 = np.arange(8)`

, it is now possible to work with `pow()`

:

```
print(pow(arr1, 2)) # [ 0 1 4 9 16 25 36 49]
```

**Important:** When working with Windows OS, an error is possible.

As we know, ( 2^{31} = 2147483648 ), but `numpy.power(2, 31)`

may return -2147483648. Even more strangely, `numpy.power(2, 32)`

will return 0.

The reason is simple, although unexpected and not really voiced: to convert the resulting number internally to a long integer (an integer larger than ( 2^{31} )), you need to forcibly specify it.

So, we fix it like this:

```
numpy.power(2, 31, dtype=numpy.longlong) # We will get 2147483648, not -2147483648
numpy.power(2, 32, dtype=numpy.longlong) # We will get 4294967296, not 0
```

## Comparing the Speed of Exponentiation Methods

Let’s compare the speed of different exponentiation methods and choose the optimal one for our tasks.

```
from timeit import timeit
# ========== ** ==========
pow1 = """
2**30
"""
elapsed_time1 = timeit(pow1, number=100_000)
# Run pow1 code 100,000 times to reduce tolerance
print(elapsed_time1)
# ========== pow() ==========
pow2 = """
pow(2, 30)
"""
elapsed_time2 = timeit(pow2, number=100_000)
print(elapsed_time2)
# ========== math.pow() ==========
pow3 = """
math.pow(2, 30)
"""
elapsed_time3 = timeit(pow3, number=100_000, setup="import math") # Run pow3 code 100,000 times
print(elapsed_time3)
# ========== numpy.power() ==========
pow4 = """
numpy.power(2, 30)
"""
elapsed_time4 = timeit(pow4, number=100_000, setup="import numpy") # Run pow4 code 100,000 times
print(elapsed_time4)
```

Based on 100,000 launches (`number=100_000`

), we get the following results:

Rank | Method | Time |
---|---|---|

1 | `2**30` | 0.0009847609 |

2 | `math.pow(2, 30)` | 0.0073750170 |

3 | `pow(2, 30)` | 0.0137873840 |

4 | `numpy.power(2, 30)` | 0.0973609160 |

Execution times may vary based on the platform the code is running on.

**Conclusion:** The `**`

operator is the simplest, fastest, and most versatile.

## A Bit of Math Magic

Mathematical calculations can be performed using Python. Here are some mathematical tricks.

#### Square Root (√)

The square root is a well-known operation:

[ \sqrt{9} = 3 ]

[ \sqrt{81} = 9 ]

But there’s a mathematical trick to compute the root without memorizing or using `sqr/sqrt`

:

```
print(9 ** 0.5) # 3.0
print(81 ** 0.5) # 9.0
```

That is, the exponent 1/2 is the square root:

[ \sqrt{64} = 64^{1/2} = 8.0 ]

### Negative Exponent

Let’s try solving a negative exponent:

```
print(7 ** -3) # 0.0029154518950437317
```

It looks scary, but it’s quite logical if you think of it as a fraction:

[ 7^{-3} = \frac{1}{7^3} ]

### Zero Exponent

Mathematical basis: Any number to the power of 0 is 1.

Example of zero exponent in code:

```
print(54532 ** 0) # 1
print(0 ** 0) # 1
```

## Conclusion

Exponentiation is one of the most commonly used operations in any programming language. In this article, we have explored three ways to calculate power in Python: the `**`

operator, the `pow()`

function, `math.pow()`

, and `numpy.power()`

.

The fastest and most versatile method is the `**`

operator. It is important to remember that much depends on the device and platform you are using. You should also consider the efficiency, readability, and future maintenance of the method.