Python gives you three ways to raise a number to a power: the ** operator, the built-in pow() function, and math.pow() from the standard library. This tutorial walks through all three, covers the edge cases that trip up beginners, and gives you hands-on exercises to lock in each concept.
What's in this Python Tutorial▼
Exponentiation means raising a base number to a power. When you write 2 raised to the power of 3, Python multiplies 2 by itself three times to get 8. The ** operator is the most direct way to express this in code, and it works on integers, floats, and complex numbers alike.
The ** Operator
Place the base on the left and the exponent on the right. Python evaluates the expression and returns the result. Two mathematical identities always hold: anything raised to the power of zero is 1, and anything raised to the power of one is itself.
result = 2 ** 3
print(result) # 8
10 ** 0 # 1 (any number to the power of 0 is 1)
5 ** 1 # 5 (any number to the power of 1 is itself)
3 ** 4 # 81
2 ** 10 # 1024
7 ** 2 # 49
Python integers have arbitrary precision, so integer exponentiation never overflows. 2 ** 100 returns the exact 31-digit integer. The **= augmented assignment operator raises a variable to a power and reassigns the result in one step.
value = 3
value **= 4
print(value) # 81 (same as value = value ** 4)
side = 5
side **= 2
print(side) # 25 (area of a square with side 5)
The ** operator is right-associative: 2 ** 3 ** 2 evaluates as 2 ** (3 ** 2) = 512, not (2 ** 3) ** 2 = 64. When chaining powers, use parentheses to make your intent explicit.
125 ** (1/3) returns 4.999... rather than exactly 5.0 due to floating-point representation. Use round() or math.isclose() when comparing fractional-exponent results against expected integers.
import math
# ** preserves int when both operands are int
type(2 ** 3) # <class 'int'>
type(2.0 ** 3) # <class 'float'>
# built-in pow() matches ** for two arguments
pow(2, 8) # 256 (int)
# pow() three-argument form: modular exponentiation
pow(2, 10, 1000) # 24 same as (2**10) % 1000, but efficient
# math.pow() always returns float
math.pow(2, 3) # 8.0 (float)
"Explicit is better than implicit." — The Zen of Python
Build a Python expression that raises base to the 8th power and assigns it to result.
result = base ** 8. The assignment target goes on the left, then =, then the base, then the exponent operator **, then the exponent 8. Using * instead of ** would multiply rather than raise to a power, and + would only add 8 to the base.
Integer vs Float Results, and pow()
When both operands are integers, ** returns an integer. When either operand is a float, the result is a float. The three exponentiation tools differ in how they handle types, and only one supports a three-argument form.
| Method | Return type (int inputs) | Three-argument form | Best used for |
|---|---|---|---|
** | int | No | General-purpose exponentiation |
pow(b, e) | int | Yes | When modular form may be needed |
math.pow(b, e) | float | No | When a float result is always required |
Three-argument pow(base, exp, mod) computes (base ** exp) % mod using fast modular exponentiation internally. This is far more efficient than computing the full power first, and is the standard approach in cryptographic algorithms.
- Return type
- Returns
intwhen both operands are integers; returnsfloatwhen either operand is a float. - Best used for
- General-purpose exponentiation in everyday expressions. Supports
**=augmented assignment.
- Return type
- Returns
intwhen both operands are integers; returnsfloatwhen either is a float. Identical to**for two arguments. - Best used for
- When you may also need the three-argument modular form:
pow(base, exp, mod). All three arguments must be integers when using the modulus parameter.
- Return type
- Always returns
float, even for integer inputs.math.pow(2, 3)returns8.0. - Best used for
- When a float result is always required regardless of input types. Note: does not accept a third argument and will raise a
TypeErrorif one is passed.
The function below contains one bug. Click the line you think is wrong, then hit check.
return (-n) ** 2. The ** operator has higher precedence than unary minus on the left, so -n ** 2 is evaluated as -(n ** 2), negating the result instead of squaring a negative base.
Negative and Fractional Exponents
A negative exponent computes the reciprocal of the positive power: b ** -n equals 1 / (b ** n). A fractional exponent computes a root: an exponent of 0.5 gives the square root, 1/3 the cube root.
# Negative exponents — result is always float
2 ** -1 # 0.5 (1 / 2)
2 ** -3 # 0.125 (1 / 8)
10 ** -2 # 0.01 (1 / 100)
# Square and cube roots via fractional exponent
16 ** 0.5 # 4.0
8 ** (1/3) # 2.0
27 ** (1/3) # 3.0
125 ** (1/3) # 4.999999999999999 (floating-point imprecision)
# Precedence: ** binds tighter than unary minus on the left
-2 ** 4 # -16 (same as -(2 ** 4))
(-2) ** 4 # 16 (use parentheses for a negative base)
Edge Cases Worth Knowing
A small set of inputs produce results that surprise many programmers the first time they encounter them. Each case below has a specific reason rooted in Python's type system or IEEE 754 floating-point rules.
# 0 ** 0 — Python returns 1 by convention (undefined in mathematics)
0 ** 0 # 1
0 ** 0.0 # 1.0
# 0.0 ** negative — raises ZeroDivisionError (division by zero)
# 0.0 ** -1 # ZeroDivisionError: 0.0 cannot be raised to a negative power
# Negative base with fractional exponent — Python 3 returns a complex number
# (the ** operator promotes to complex rather than raising an error)
(-8) ** (1/3) # (1.0000000000000002+1.7320508075688772j) — NOT -2.0
(-8) ** 0.5 # (2.220446049250313e-16+2.8284271247461903j)
# math.pow() raises ValueError for a negative base with a fractional exponent
# import math
# math.pow(-8, 1/3) # ValueError: math domain error
# Use the unary minus trick for odd integer roots of negative numbers
-(8 ** (1/3)) # -2.0 (manually negate after computing on the positive base)
In Python 3, the ** operator follows standard complex arithmetic: any real number raised to a non-integer power can yield a complex result when the base is negative. The interpreter does not assume you want the real cube root; it computes the principal value. If you need the real cube root of a negative number, negate after computing on the positive base: -(8 ** (1/3)). For complex number exponentiation more broadly, use Python's built-in complex type or the cmath module.
When floating-point imprecision cannot be tolerated — financial calculations, for example — use Python's decimal.Decimal type. It supports the ** operator and delivers user-controlled precision without IEEE 754 rounding artifacts:
from decimal import Decimal, getcontext
getcontext().prec = 50 # set precision to 50 significant digits
Decimal('1.1') ** 2 # Decimal('1.21') — exact
1.1 ** 2 # 1.2100000000000002 — float imprecision
The Three-Argument pow() and Why It Matters
The three-argument form pow(base, exponent, modulus) does not simply compute base ** exponent and then take the remainder. Internally Python uses the binary exponentiation algorithm (also called fast exponentiation or exponentiation by squaring), which reduces a large power computation to a series of squarings and multiplications — so the intermediate numbers never grow large. All three arguments must be integers, and the exponent cannot be negative when a modulus is provided.
# Efficient modular exponentiation — used in RSA and Diffie-Hellman
pow(2, 10, 1000) # 24 — (2**10) % 1000, but computed efficiently
pow(65537, 2048, 3233) # fast — RSA-style computation on large integers
# These will raise exceptions:
# pow(2.5, 3, 7) # TypeError: all three arguments must be integers
# pow(2, -1, 7) # ValueError: base is not invertible for given modulus
# pow(2+3j, 2, 5) # TypeError: complex modulo not supported
How Python's ** Operator Works Internally
When Python evaluates x ** y, it calls the __pow__ method on the left operand. If that method returns NotImplemented, Python calls __rpow__ on the right operand. This means any class can support the ** operator by implementing these dunder methods — which is exactly how libraries like NumPy make array exponentiation work seamlessly with the same syntax.
# Python calls x.__pow__(y) first; if that returns NotImplemented,
# it calls y.__rpow__(x) as a fallback.
(2).__pow__(8) # 256 — same as 2 ** 8
(2).__pow__(8, 1000) # 256 % 1000 = 256 — __pow__ also accepts a modulus
# A minimal custom class that supports **
class Scalar:
def __init__(self, value):
self.value = value
def __pow__(self, exponent):
return Scalar(self.value ** exponent)
def __repr__(self):
return f"Scalar({self.value})"
Scalar(3) ** 4 # Scalar(81)
How to Use Exponentiation in Python
Follow these steps to raise numbers to a power correctly, choose the right method, and avoid common precedence mistakes.
-
Use the ** operator for standard exponentiation
Write
base ** exponentto raise the base to the given power. For integer operands the result is an integer. Use**=to update a variable in place:x **= 2squaresx. -
Use pow() when you may need modular exponentiation
Call
pow(base, exponent)for the same result as**. When you also need a modulus, use the three-argument form:pow(base, exponent, modulus). Python uses the fast exponentiation algorithm internally, making this far more efficient than computing the full power and then taking the remainder. -
Use fractional exponents for roots, with parentheses
Raise a number to a fractional power to compute roots:
16 ** 0.5is the square root,8 ** (1/3)is the cube root. Always wrap fractional exponents in parentheses so Python does not misinterpret operator order. Results are floats and may carry small imprecision; useround()ormath.isclose()for comparisons.
The Python Software Foundation's language reference documents that ** groups right-to-left and that unary minus has lower precedence — meaning -x ** n always means -(x ** n). When intent matters, use parentheses. — Python Language Reference: Expressions
Python Learning Summary Points
- The
**operator is right-associative with higher precedence than*,/,+, and-. When mixing operators or using a negative base, use parentheses to make intent explicit. - Built-in
pow()matches**for two arguments and adds efficient modular exponentiation with a third argument.math.pow()always returns a float and has no three-argument form. - Negative exponents return the reciprocal as a float. Fractional exponents compute roots but may carry small floating-point errors that require
round()ormath.isclose()for comparisons. 0 ** 0returns1in Python by convention. Raising0.0to a negative power raisesZeroDivisionError. A negative base with a fractional exponent returns a complex number — not an error — under**, whilemath.pow()raisesValueErrorin that case.- Three-argument
pow(base, exp, mod)uses binary exponentiation internally, keeping intermediate values small. All three arguments must be integers; a float in any position raisesTypeError. - For precision-critical calculations where floating-point rounding is not acceptable, use
decimal.Decimalwith a configured precision context instead of**on floats. - Python evaluates
x ** yby callingx.__pow__(y). If that returnsNotImplemented, Python falls back toy.__rpow__(x). Implementing these dunder methods lets any class support the**operator transparently.
Exponentiation appears throughout Python work: computing areas and volumes, applying compound interest formulas, generating powers of two for bitmasks and buffer sizes, and forming the backbone of cryptographic algorithms through modular exponentiation.
Frequently Asked Questions
The exponentiation operator in Python is **. It raises the left operand to the power of the right operand. For example, 2 ** 3 evaluates to 8.
The ** operator and built-in pow() return the same result for two arguments. pow() also accepts a third argument for modular exponentiation: pow(base, exp, mod). math.pow() always returns a float, while ** and built-in pow() preserve integer types when both operands are integers.
Use a fractional exponent of 0.5: value ** 0.5. For example, 16 ** 0.5 returns 4.0. You can also use math.sqrt() or math.pow(value, 0.5).
Python applies ** before unary minus on the left side, so -2 ** 2 is evaluated as -(2 ** 2) = -4. To square a negative number, use parentheses: (-2) ** 2 returns 4.
Modular exponentiation computes (base ** exponent) % modulus efficiently without computing the full power first. In Python, use the three-argument form: pow(base, exponent, modulus). This is commonly used in cryptography where the numbers involved are very large integers.
In Python, 0 ** 0 returns 1 and 0 ** 0.0 returns 1.0. This is a computational convention rather than a mathematical truth — zero to the power of zero is technically undefined in mathematics — but Python follows the same convention as most other languages and the Python Software Foundation documentation. Raising 0.0 to a negative power raises ZeroDivisionError.
In Python 3, raising a negative number to a non-integer exponent follows complex arithmetic and returns the principal complex root rather than a real number. This is correct mathematical behavior — Python does not assume you want the real cube root. To get the real cube root of a negative number, negate after computing on the positive base: -(8 ** (1/3)) returns -2.0. Note also that math.pow() raises ValueError in this case rather than returning a complex number.
For comparisons, use math.isclose() or round() rather than ==. For example, 125 ** (1/3) returns 4.999... rather than exactly 5. For calculations where floating-point rounding is unacceptable — such as financial math — use decimal.Decimal with a configured precision context, which supports ** and delivers exact arithmetic.
The three-argument pow(base, exp, mod) raises a TypeError if any argument is a float rather than an integer. It raises ValueError if the exponent is negative and the base is not invertible for the given modulus, or if complex numbers are used with a modulus. Two-argument pow(base, exp) and the ** operator raise ZeroDivisionError when 0.0 is raised to a negative power.
When Python evaluates x ** y, it calls x.__pow__(y). If that method returns NotImplemented, Python falls back to y.__rpow__(x). This protocol allows any class to support ** by implementing these dunder methods — which is how libraries like NumPy make array exponentiation work with the same operator syntax.