x ** 2 is the idiomatic way to square a number in Python. x * x is equally correct and marginally faster for a single value. Avoid math.pow(x, 2) — it converts to float and returns a float, so squaring a large integer silently loses precision, and squaring an int gives you 25.0 where you wanted 25.
It is a one-line question with a genuinely interesting answer, because the four ways to do it differ in type behaviour in ways that bite outside the toy case.
Table of contents
- The four options
- Why math.pow is the wrong default
- The three-argument pow, which is genuinely useful
- Squaring collections
- Squares of decimals and money
- Performance, briefly and honestly
- How this fits the rest of the stack
- FAQ
The four options
x = 5
x ** 2 # 25 -- idiomatic, keeps int
x * x # 25 -- fastest for one value, keeps int
pow(x, 2) # 25 -- builtin, keeps int
import math
math.pow(x, 2) # 25.0 -- always float
The first three preserve the type. math.pow does not, and that difference is the whole story.
For negative numbers, watch the operator precedence — ** binds more tightly than unary minus:
-3 ** 2 # -9 -- this is -(3 ** 2)
(-3) ** 2 # 9 -- what you probably meant
This catches people in formulas transcribed from a textbook, where the intent is unambiguous on paper and not in code.
Why math.pow is the wrong default
Python integers have unlimited precision. Floats have 53 bits of mantissa. math.pow converts to float first, so anything above roughly 2^53 loses accuracy.
import math
n = 9007199254740993 # 2**53 + 1
n ** 2
# 81129638414606690193151041168897
math.pow(n, 2)
# 8.112963841460669e+31 -- wrong, and silently so
int(math.pow(n, 2)) == n ** 2
# False
No exception, no warning. The result is approximately right and exactly wrong, which is the most dangerous kind of numeric bug. If the value is an ID, a checksum, a currency amount in minor units, or anything compared for equality, this breaks in a way that is very hard to trace.
math.pow also raises on cases ** handles fine:
(-8) ** (1/3) # a complex-ish float result
math.pow(-8, 1/3) # ValueError: math domain error
2 ** 1000 # exact, 302 digits
math.pow(2, 1000) # OverflowError
The only reason to reach for math.pow is when you specifically want float semantics and C-library behaviour. That is a real but narrow case.
The three-argument pow, which is genuinely useful
The builtin pow takes an optional modulus, computing (base ** exp) % mod without ever building the enormous intermediate.
pow(7, 2, 5) # 4 -- (49) % 5
# The real use: modular exponentiation in cryptography
pow(base, exponent, modulus)
# Modular inverse, Python 3.8+
pow(3, -1, 11) # 4, because (3 * 4) % 11 == 1
Computing pow(2, 10**6) % n the naive way allocates a number with about 300,000 digits before taking the remainder. The three-argument form does it in microseconds with constant memory. This is why pow exists as a builtin rather than only as an operator.
Squaring collections
nums = [1, 2, 3, 4, 5]
# List comprehension -- the idiomatic choice
squares = [n ** 2 for n in nums]
# Lazy, for large or infinite sources
squares = (n ** 2 for n in nums)
# map, when you already have a named function
squares = list(map(lambda n: n ** 2, nums))
# Filter and transform together
even_squares = [n ** 2 for n in nums if n % 2 == 0]
The comprehension is clearer than map with a lambda and usually faster, because it avoids the per-element function call. Use map when you are passing an existing function by name.
For numeric arrays, NumPy is a different order of magnitude:
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
arr ** 2 # array([ 1, 4, 9, 16, 25])
np.square(arr) # same, marginally faster
# In place, no new allocation
np.square(arr, out=arr)
NumPy operates on the whole array in compiled code rather than looping in Python. On a million elements that is the difference between milliseconds and a noticeable pause. It also has the opposite overflow behaviour from pure Python — a fixed-width int64 array wraps around silently rather than growing, which is worth knowing before you trust a large result.
Squares of decimals and money
Float arithmetic is approximate, and squaring compounds the error.
0.1 ** 2 # 0.010000000000000002
from decimal import Decimal
Decimal('0.1') ** 2 # Decimal('0.01') -- exact
from fractions import Fraction
Fraction(1, 3) ** 2 # Fraction(1, 9) -- exact
For money, use Decimal — or better, store amounts as integers in minor units and avoid the question entirely. For exact rational arithmetic, Fraction does not accumulate error at all.
Comparing float results for equality is the related trap. 0.1 ** 2 == 0.01 is False. Use math.isclose(a, b) when comparing floats, always.
Performance, briefly and honestly
python -m timeit -s "x = 5" "x * x"
python -m timeit -s "x = 5" "x ** 2"
python -m timeit -s "import math; x = 5" "math.pow(x, 2)"
x * x is typically fastest, x ** 2 close behind, math.pow slowest because of the function call and the float conversion. The differences are nanoseconds and matter only inside a hot loop running millions of times.
If you are in that situation, the answer is usually not micro-optimising the operator — it is NumPy, or moving the loop out of Python. Choosing x * x over x ** 2 for speed in ordinary code is optimising the wrong thing; choose whichever reads better where it appears.
And when the computation genuinely is the bottleneck, that is a sizing question rather than a syntax one. A worker doing heavy numeric work needs CPU and memory headroom, which is a plan decision — on RunxBuild that means picking a plan with the vCPU and RAM the job actually needs, with autoscaling between a floor and a ceiling for work that arrives in bursts.
How this fits the rest of the stack
Use x ** 2. Use x * x if you prefer how it reads. Use the builtin pow when you need the three-argument modular form, which is genuinely valuable. Avoid math.pow unless you specifically want float semantics, because it silently loses integer precision above 2^53.
Watch the precedence on -3 ** 2, use Decimal for money, and reach for NumPy when squaring arrays rather than looping. If the numeric work is heavy enough to need real CPU, the RunxBuild hosting calculator shows what the service, database, storage, and bandwidth cost as separate line items.
Useful related references:
- Python Not Equal: != vs is not, and Why the Difference Bites
- Python Integer Division: Why // Floors and Why -7 // 2 Is -4
- Python for Websites: Where It Fits and Where It Does Not
- Python services on RunxBuild
FAQ
How do you square a number in Python?
Use the exponent operator: x ** 2. Multiplying by itself with x * x is equally correct and slightly faster for a single value. Both preserve the type, so squaring an integer gives an integer.
What is the difference between ** and math.pow in Python?
** preserves types and uses Python’s unlimited-precision integers. math.pow converts both arguments to float and always returns a float, so squaring an integer gives 25.0 rather than 25, and integers above 2^53 lose precision silently with no error raised.
Why does -3 ** 2 give -9 in Python?
The exponent operator binds more tightly than unary minus, so the expression is parsed as -(3 ** 2). Write (-3) ** 2 to square the negative number. This catches people transcribing formulas where the intent is obvious on paper.
What is the third argument to pow() in Python?
A modulus. pow(base, exp, mod) computes (base ** exp) % mod efficiently without building the huge intermediate result, which is essential for cryptographic work. Since Python 3.8 a negative exponent with a modulus also gives the modular inverse.
How do I square every number in a list in Python?
Use a list comprehension: [n ** 2 for n in nums]. For large numeric arrays use NumPy — arr ** 2 operates on the whole array in compiled code, which is dramatically faster, though fixed-width integer arrays wrap on overflow instead of growing.