Python ceil() Function: How to Round Numbers Up

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
Command Prompt output of the Python math ceil function rounding 2.1, 2.9, 3.0 and -2.8 and showing that the result is an int
math.ceil() returns an int, so it is ready to use as a count or an index.
CallResultWhy
math.ceil(2.1)3the next whole number up
math.ceil(2.9)3same, however close it already is
math.ceil(3.0)3already whole, unchanged
math.ceil(-2.8)-2up means towards positive infinity
math.ceil(7)7an 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
Command Prompt table comparing Python ceil, floor, int, round and trunc for positive and negative values
The 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
Command Prompt output showing math.ceil of a big integer division returning a different and wrong value compared with negative floor division
With 1017, 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
Command Prompt output showing Python ceil returning 4 for 0.1 plus 0.2 times ten, and Decimal with ROUND_CEILING rounding a price up to the next cent
Round away the binary noise first, or use 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
Command Prompt output of numpy ceil on an array and a pandas DataFrame column of hours rounded up to billed hours
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
InputExceptionMessage
float("inf")OverflowErrorcannot convert float infinity to integer
float("nan")ValueErrorcannot convert float NaN to integer
"3.2"TypeErrormust be real number, not str
NoneTypeErrormust 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:

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.