Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Exponentiation in Python: ** Is the Operator, and pow Does Modular Math

Sean

Platform Writer

Jul 18, 2026
6 min read

To raise a number to a power in Python, use the ** operator: 2 ** 10 is 1024. That is the everyday way, and it works on integers and floats. There are two other tools worth knowing: pow(base, exp) does the same thing as a function, and pow(base, exp, mod) computes (base ** exp) % mod efficiently, which matters enormously in cryptography. One trap: math.pow always returns a float, so math.pow(2, 3) is 8.0, not 8 - use ** when you want an integer result.

Exponentiation in Python: ** Is the Operator, and pow Does Modular Math

Table of contents

The ** operator

2 ** 10       # 1024
5 ** 2        # 25
2 ** 0.5      # 1.4142135623730951  - square root, via a fractional power
10 ** -2      # 0.01                - negative exponent, a reciprocal

** is the exponentiation operator: base ** exponent. It handles integer powers, fractional powers (a fractional exponent is a root - x ** 0.5 is the square root), and negative powers (which give reciprocals).

A nice property of ** on integers: it stays an integer and Python’s integers are unbounded, so 2 ** 1000 computes exactly, no overflow:

2 ** 100      # 1267650600228229401496703205376 - exact, arbitrary precision

This is genuinely useful - many languages overflow a 64-bit integer here and give a wrong answer. Python just computes the whole number. For pure integer exponentiation, ** is the right tool and it will not lie to you about large results.

pow, the function form

pow(base, exp) is the built-in function equivalent of **:

pow(2, 10)      # 1024  - identical to 2 ** 10

For two arguments, pow and ** do exactly the same thing; use whichever reads better. ** is more common in inline expressions; pow is handy when you need a function to pass around, for example as a key or in a map.

The reason pow earns its own function rather than being pure syntax sugar is its three-argument form, covered next. That third argument is something the operator cannot express, and it is the whole reason pow is worth remembering as distinct from **. For the two-argument case, treat them as interchangeable and pick by readability.

Three-argument pow: modular exponentiation

This is the feature that makes pow special. pow(base, exp, mod) computes (base ** exp) % mod, but efficiently - without ever building the gigantic intermediate number:

pow(2, 10, 1000)          # 24  = 1024 % 1000
pow(7, 256, 13)           # a modular power, computed fast

Why it matters: in cryptography you routinely raise huge numbers to huge powers modulo another huge number. Computing base ** exp first and then taking the modulus would produce an astronomically large intermediate value - millions of digits - and be hopelessly slow. Three-argument pow uses modular exponentiation by squaring, which keeps every intermediate value small.

# RSA-style operation, feasible only because of 3-arg pow
pow(message, exponent, modulus)

If you are doing anything with modular arithmetic - cryptography, hashing schemes, number theory - three-argument pow is not a nice-to-have, it is the only practical way. This is the one piece of Python exponentiation that people genuinely do not know exists, and it is the most valuable.

math.pow returns a float

There is a third exponentiation tool, math.pow, and it has a catch:

import math
math.pow(2, 3)      # 8.0   - a float, always
2 ** 3              # 8     - an int

math.pow always returns a float, even for integer inputs. It also cannot handle Python’s arbitrary-precision integers - it converts to float first, so math.pow(2, 1000) overflows the float and raises an error, while 2 ** 1000 computes exactly.

The practical guidance: prefer ** and built-in pow over math.pow for most work. Use ** when you want an integer result and exact large-integer math. Reach for math.pow only when you specifically want float semantics and are working within float range. The float return of math.pow catches people who expected an integer and got 8.0 - if a downstream operation needs an int, that trailing .0 is a real bug waiting to happen.

Roots, and common patterns

Exponentiation covers roots and a few recurring needs:

# square root - three ways
16 ** 0.5           # 4.0   - fractional power
import math
math.sqrt(16)       # 4.0   - dedicated, clearer for square roots

# cube root
27 ** (1/3)         # 3.0000000000000004  - float imprecision, note

# nth root
x ** (1/n)

For a square root specifically, math.sqrt is clearer and more accurate than x ** 0.5, so prefer it when you mean square root. For other roots, x ** (1/n) is the general form, but watch the floating-point imprecision - 27 ** (1/3) comes out as 3.0000000000000004, not a clean 3, because 1/3 cannot be represented exactly and the whole thing is float math.

That imprecision is worth remembering: a computed cube root that should be a whole number often is not, by a tiny amount. If you need to test whether a number is a perfect cube, do not compare the float root to an integer directly - round it and check, or work with integers. It is the same floating-point reality that makes round(2.675, 2) surprising, showing up in a different place.

How this fits the rest of the stack

Knowing that three-argument pow is the only practical way to do modular exponentiation is the kind of detail that separates code that works from code that hangs on real key sizes. When cryptographic or numeric work runs as a service, using the efficient primitive instead of the naive one is the difference between a fast response and a timeout. The RunxBuild hosting calculator lays out the service, database, storage, and bandwidth as separate line items, and the RunxBuild dashboard is where the team watches deploys, logs, and restarts as they happen.

Useful related references:

FAQ

How do I raise a number to a power in Python?

Use the ** operator: 2 ** 10 is 1024. It works on integers and floats, handles fractional exponents as roots (x ** 0.5 is the square root) and negative exponents as reciprocals. The built-in pow(base, exp) does the same thing as a function.

What is the difference between ** and pow in Python?

For two arguments they are identical - 2 ** 10 equals pow(2, 10). The difference is pow’s three-argument form: pow(base, exp, mod) computes (base ** exp) % mod efficiently, which the operator cannot express and which is essential for modular arithmetic in cryptography.

What does three-argument pow do?

pow(base, exp, mod) computes (base ** exp) % mod using fast modular exponentiation, without ever building the huge intermediate base ** exp. It is essential in cryptography, where raising large numbers to large powers modulo another number is routine and the naive approach would be impossibly slow.

Why does math.pow return a float?

math.pow always returns a float by design, so math.pow(2, 3) is 8.0, not 8. It also converts to float internally, so it cannot do exact large-integer math. Use the ** operator when you want an integer result or arbitrary-precision integer exponentiation.

How do I compute a square root in Python?

Use math.sqrt(x), which is clearer and more accurate for square roots, or x ** 0.5 as a general fractional power. For other roots use x ** (1/n), but expect small floating-point imprecision - 27 ** (1/3) comes out slightly off from 3.

#exponentiation in python#python#power operator#pow#dev-infra