The Python ceil() function rounds a number up to the nearest whole number. It lives in the math module, so you call it as math.ceil(x) after import math: math.ceil(2.1) gives 3, and math.ceil(-2.8) gives -2, because up means towards positive infinity. It always returns an int, never a float. This guide covers the ceiling function with positive and negative numbers, the integer-division version that does not lose precision, rounding money up, NumPy and pandas columns, and the errors you will hit.
Every example was run with Python 3.12.5 (NumPy 2.5.3 and pandas 3.0.6) in the Windows Command Prompt, and the output shown is the real output. Reference: math.ceil() in the Python documentation.
The Python ceil() function
ceil() is not a built-in: it has to be imported from math first. After that it takes one number and gives you the smallest whole number that is not less than it:
import math
print(math.ceil(2.1)) # up to the next whole number
print(math.ceil(2.9))
print(math.ceil(3.0)) # already whole, nothing changes
print(math.ceil(-2.8)) # "up" means towards zero here
result = math.ceil(4.2)
print(result, type(result).__name__) # always an int, never a float
Output:
3
3
3
-2
5 int
math.ceil() returns an int, so it is ready to use as a count or an index.| Call | Result | Why |
|---|---|---|
math.ceil(2.1) | 3 | the next whole number up |
math.ceil(2.9) | 3 | same, however close it already is |
math.ceil(3.0) | 3 | already whole, unchanged |
math.ceil(-2.8) | -2 | up means towards positive infinity |
math.ceil(7) | 7 | an int passes straight through |
Python ceil() syntax and what it accepts
The signature is math.ceil(x), where x is any real number. That includes Decimal and Fraction values, and any object of your own that defines a __ceil__ method, which is how the function stays useful outside plain floats:
import math
from decimal import Decimal
from fractions import Fraction
print(math.ceil(7)) # an int comes back unchanged
print(math.ceil(Decimal("2.0001"))) # Decimal works
print(math.ceil(Fraction(7, 3))) # so does Fraction: 7/3 is 2.33...
print(math.ceil(True)) # bool is an int in Python
class Delivery:
"""Any object can support ceil() by defining __ceil__."""
def __init__(self, hours: float) -> None:
self.hours = hours
def __ceil__(self) -> int:
return math.ceil(self.hours)
print(math.ceil(Delivery(4.2)))
Output:
7
3
3
1
5
In Python 2, math.ceil() returned a float. In Python 3 it returns an int, so you no longer need int(math.ceil(x)): that extra call does nothing.
ceil() with negative numbers, and how it differs from floor(), round() and int()
Negative numbers are where the four functions part company. ceil() always moves towards positive infinity, floor() always moves towards negative infinity, int() and math.trunc() chop the decimals off towards zero, and round() goes to the nearest even number on a tie:
import math
values = [2.4, 2.5, 2.6, -2.4, -2.5, -2.6]
print(f"{'value':>6} {'ceil':>6} {'floor':>6} {'int':>6} {'round':>6} {'trunc':>6}")
for value in values:
print(f"{value:>6} {math.ceil(value):>6} {math.floor(value):>6} {int(value):>6} "
f"{round(value):>6} {math.trunc(value):>6}")
Output:
value ceil floor int round trunc
2.4 3 2 2 2 2
2.5 3 2 2 2 2
2.6 3 2 2 3 2
-2.4 -2 -3 -2 -2 -2
-2.5 -2 -3 -2 -2 -2
-2.6 -2 -3 -2 -3 -2
2.5 and -2.5 rows show banker’s rounding in round().So ceil() and int() agree on negative values and differ on positive ones, which is exactly the opposite of what most people assume.
Real work: pages, billing blocks and boxes
Almost every practical use of the ceiling function is “how many whole units do I need to cover this”. Dividing gives a fraction, and you cannot ship 11.4 boxes:
import math
items = 47
per_page = 10
print("pages needed:", math.ceil(items / per_page))
minutes = 23
print("billed 15-minute blocks:", math.ceil(minutes / 15))
# round a value up to the next multiple of 5
for length in (11, 15, 16):
print(length, "->", math.ceil(length / 5) * 5)
boxes = math.ceil(137 / 12) # 137 mugs, 12 to a box
print("boxes to ship:", boxes, "| empty slots:", boxes * 12 - 137)
Output:
pages needed: 5
billed 15-minute blocks: 2
11 -> 15
15 -> 15
16 -> 20
boxes to ship: 12 | empty slots: 7
The same pattern rounds a value up to the next multiple: divide by the step, take the ceiling, multiply back.
Ceiling division without math.ceil()
There is an integer-only version of the same idea: -(-a // b). It uses the double slash floor-division operator twice, so the answer never passes through a float. For everyday numbers both give the same result. For big integers, only one of them is right:
import math
items, per_page = 47, 10
print(math.ceil(items / per_page), -(-items // per_page)) # same answer, two ways
# with big integers, the float division inside math.ceil loses precision
big = 10 ** 17
print("math.ceil(big / 3) =", math.ceil(big / 3))
print("-(-big // 3) =", -(-big // 3))
print("equal?", math.ceil(big / 3) == -(-big // 3))
print("exact remainder:", big % 3)
Output:
5 5
math.ceil(big / 3) = 33333333333333332
-(-big // 3) = 33333333333333334
equal? False
exact remainder: 1
math.ceil(big / 3) is off by two: the float lost the precision before ceil() ever ran.A Python int has no size limit, but a float carries only about 15 to 17 significant digits. big / 3 is rounded to a float before math.ceil() sees it, so the result is wrong while looking perfectly reasonable. Any time both operands are integers, prefer -(-a // b).
Floats round up when you do not expect it
The other float surprise is smaller but far more common. 0.1 + 0.2 is not exactly 0.3, so multiplying by ten lands just above 3 and ceil() dutifully returns 4. The same thing happens with money and two decimal places:
import math
x = 0.1 + 0.2
print(x) # not exactly 0.3
print(math.ceil(x * 10)) # 4, although 0.3 * 10 should be 3
print(1.1 * 3, math.ceil(1.1 * 3))
# fix it by rounding away the noise first, or by not using floats at all
print(math.ceil(round(x * 10, 9)))
from decimal import Decimal, ROUND_CEILING
price = Decimal("12.341")
print(price.quantize(Decimal("0.01"), rounding=ROUND_CEILING)) # always up to the next cent
Output:
0.30000000000000004
4
3.3000000000000003 4
3
12.35
Decimal where the cents have to be exact.For prices, Decimal with ROUND_CEILING is the honest tool: it rounds up to the next cent without a float ever being involved.
ceil() for NumPy arrays and pandas columns
math.ceil() takes a single number, so passing an array raises a TypeError. NumPy has its own np.ceil(), which works element by element and keeps the float dtype, so add .astype(int) when you want whole numbers. In pandas the same function applies to a whole column, which is how float columns become integer counts:
import math
import numpy as np
import pandas as pd
hours = np.array([1.2, 3.5, -0.4])
print(np.ceil(hours), np.ceil(hours).dtype) # NumPy keeps floats
print(np.ceil(hours).astype(int)) # convert if you want ints
try:
math.ceil(hours) # math.ceil takes one number
except TypeError as error:
print("TypeError:", error)
timesheet = pd.DataFrame({"consultant": ["Emma", "Michael", "Olivia"],
"hours": [1.2, 3.5, 0.1]})
timesheet["billed"] = np.ceil(timesheet["hours"]).astype(int)
print(timesheet.to_string(index=False))
Output:
[ 2. 4. -0.] float64
[2 4 0]
TypeError: only 0-dimensional arrays can be converted to Python scalars
consultant hours billed
Emma 1.2 2
Michael 3.5 4
Olivia 0.1 1
np.ceil() on a column turns 1.2 and 3.5 hours into 2 and 4 billed hours.Round up to a number of decimal places
ceil() only rounds to whole numbers, so to round up to two decimals you scale the value, take the ceiling, and scale back. That works for display values; for anything you will bill someone for, do the same job with Decimal so no float ever touches the cents:
import math
from decimal import Decimal, ROUND_CEILING
def ceil_to(value: float, digits: int = 0) -> float:
"""Round up to a fixed number of decimal places."""
factor = 10 ** digits
return math.ceil(value * factor) / factor
print(ceil_to(12.341, 2), ceil_to(12.3401, 2), ceil_to(0.0001, 2))
print(ceil_to(-12.341, 2))
print(ceil_to(7.2)) # no digits: a whole number, as a float
def ceil_money(value: str, digits: int = 2) -> Decimal:
"""The same job for prices, without floats."""
step = Decimal(1).scaleb(-digits) # 0.01 for 2 digits
return Decimal(value).quantize(step, rounding=ROUND_CEILING)
print(ceil_money("12.341"), ceil_money("12.340"), ceil_money("-12.341"))
Output:
12.35 12.35 0.01
-12.34
8.0
12.35 12.34 -12.34
Notice that the Decimal version of -12.341 rounds up to -12.34, towards zero, because ceiling always means towards positive infinity.
Errors from math.ceil()
Three inputs fail, each with its own exception, and the messages are worth recognising:
import math
for value in (float("inf"), float("nan"), "3.2", None):
try:
math.ceil(value)
except Exception as error:
print(f"{value!r:>6} -> {type(error).__name__}: {error}")
Output:
inf -> OverflowError: cannot convert float infinity to integer
nan -> ValueError: cannot convert float NaN to integer
'3.2' -> TypeError: must be real number, not str
None -> TypeError: must be real number, not NoneType
| Input | Exception | Message |
|---|---|---|
float("inf") | OverflowError | cannot convert float infinity to integer |
float("nan") | ValueError | cannot convert float NaN to integer |
"3.2" | TypeError | must be real number, not str |
None | TypeError | must be real number, not NoneType |
A string is the common one: convert it first with float("3.2"), then take the ceiling.
More Python number-handling guides you may find useful:
- Round numbers in Python
- The pow() method in Python
- Divide two numbers in Python
- Remove decimal numbers from a value
- Convert float columns to integers in pandas
Frequently asked questions
What does ceil() do in Python?
It rounds a number up to the nearest whole number and returns an int. math.ceil(4.1) is 5, and math.ceil(4.0) stays 4.
How do I import ceil in Python?
Write import math and call math.ceil(x), or from math import ceil to call ceil(x) directly. There is no built-in ceil().
Does math.ceil() return an int or a float?
An int in Python 3. It returned a float in Python 2, which is why older code wraps it in int() unnecessarily.
Why does math.ceil(-2.8) return -2?
Rounding up means towards positive infinity, and -2 is greater than -2.8. Use math.floor() to go the other way.
What is the difference between ceil() and int()?
int() chops the decimals off towards zero, so int(2.8) is 2 while math.ceil(2.8) is 3. On negatives they agree.
How do I do ceiling division in Python?
Use -(-a // b) for integers. It avoids floats entirely, so it stays exact even for very large numbers, unlike math.ceil(a / b).
How do I round up a whole NumPy array or pandas column?
Use np.ceil(values), which works element by element. Add .astype(int) if you need integers; math.ceil() only takes one number.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn