Python Round Function: Rounding Numbers in Python

By Sruthy

By Sruthy

Sruthy, with her 10+ years of experience, is a dynamic professional who seamlessly blends her creative soul with technical prowess. With a Technical Degree in Graphics Design and Communications and a Bachelor’s Degree in Electronics and Communication, she brings a unique combination of artistic flair…

Learn about our editorial policies.
Updated March 7, 2024

This tutorial on Rounding Numbers in Python explains different methods to round numbers with multiple programming code examples:

Computers were originally invented for complex math operations and we didn’t bother as long as integers, subtraction, addition, and multiplication were involved. However, things became unpredictable when floating points and division got involved.

One of the reasons for this unpredictability of floating points or division is mentioned in Python’s documentation. We humans mostly work with the decimal system, which has base 10. However, the computer uses and stores the binary system(0s and 1s).

=> Check ALL Python Tutorials Here

Coming back to Python’s documentation, this is what it has to say;

Unfortunately, most decimal fractions cannot be represented exactly as binary fractions. A consequence is that, in general, the decimal floating-point numbers you enter are only approximated by the binary floating-point numbers actually stored in the machine.

Rounding Numbers In Python

FYI, this is in the very nature of binary floating-point and not a bug in Python. So most, if not all programming languages like Perl, C, C++, Java, Fortran often won’t display the exact decimal number you expect.

Suggested Reading =>> Python Built-in Data Types- None and Numeric

Rounding Numbers in Python

In this article, we shall learn the various ways of rounding numbers in Python.

Floating-Point Numbers

This article is all about rounding numbers. But which numbers? In this section, we are going to have a quick look at these numbers.

A floating-point number is basically any number with a decimal point dividing the integer from the fractional parts. Represented as a 64-bit double-precision in Python, it has the data type ‘float’ and can also be represented in scientific notations, with e or E indicating the power 10. For example (1.4E4 = 1.4 x 104 = 14000)

In the introduction, we mentioned that most decimal fractions can’t be represented exactly as binary fractions. Let’s take a look at the example below.

Example 1: Unpredictability of floating-point numbers

>>> 0.1 + 0.1 + 0.1 # expected 0.3
0.30000000000000004
>>> 1/10
0.1
>>> 1/10 == 0.10000000000000001 # expected False
True
>>> format(0.1, '.17f') # expland to 17 decimal places
'0.10000000000000001'

From the example above, the first line of code(addition of 0.1s) didn’t return the expected result of 0.3. Also, we see that 1/10 equals 0.1, but surprisingly, the equality operator in the third line of code returned True. However, the last line of code reveals the real value of 0.1 in 17 decimal places.

The simple reason is that both results are just the nearest approximate binary fractions. And interestingly, in Python, there are many different decimal numbers that share the same binary fraction. For example, the numbers 0.1 and 0.10000000000000001 as we saw above.

This example above shows us how floating-point numbers are inaccurate and thus tricky to work with. This is where rounding numbers can spare us a lot by minimizing computing errors.

Rounding Numbers

Wikipedia defines rounding to be the replacement of a number with an approximate value that has a shorter, simpler, or more explicit representation. For example, replacing 4.345 with 4.3, 3.67 with 4.

Still from Wikipedia, it is mentioned that rounding is often done to obtain a value that is easier to report and communicate than the original. But this usually comes with round-off errors.

Most of us were taught two simple rules to apply when rounding numbers. If the digit after the decimal digit we are aiming for is between 0 and 4 inclusively, round down(add 0), if it is between 5 and 9 inclusively, round up(add 1).

Check the diagram below for a demonstration:

Rounding Numbers

Say we have a floating-point number 3.45678 and we want to round it to n decimal places, in this case, 3. From the diagram above, we have 6 as our target decimal number because shifting the decimal point 3 times to the right will be 346.78. The number after the decimal point, in this case, is 7, which falls between 5 and 9. Hence, we round up by adding 1 to 6.

Finally, we discard all numbers after the decimal point and bring them back to their normal position, which gives us 3.457

One of the built-in functions provided by Python to handle rounding numbers is the round() function (more details below).

Built-in Python round() Function

The round() function is a Python built-in function that takes in two numeric arguments, number and an optional ndigit, then returns the number, n rounded to a given precision in decimal digits, ndigit.

In other words, it rounds the number to the closest multiple of 10-ndigits

Syntax:

round(number, [, ndigit])
  • If ndigit is omitted or None, the return value will be the nearest integer.
  • ndigit can be negative, which may not give us the results we had in mind.

Let’s now see an example that shows the various ways the round() function can be used:

Example 2: Using the round() function

def rounding_numbers():
    #ex_1 ndigits is None, which is same as no ndigit given
    number = 1.5 
    ndigits = None
    print("Round {} to {} ndigits: {}".format(number, ndigits, round(number, ndigits)))

    #ex_2 no ndigits is given, which is same as ndigits = None
    number = 2.5
    print("Round {}: {}".format(number, round(number)))

    #ex_3 round to 3 decimal places
    number = 3.45678
    ndigits = 3
    print("Round {} to {} ndigits: {}".format(number, ndigits, round(number, ndigits)))

    #ex_4 round to zero decimal places
    number = 35.45678
    ndigits = 0
    print("Round {} to {} ndigits: {}".format(number, ndigits, round(number, ndigits)))

    #ex_5 round to a nagative decimal place
    number = 12.56
    ndigits = -1
    print("Round {} to {} ndigits: {}".format(number, ndigits, round(number, ndigits)))

if __name__ == '__main__':
    rounding_numbers()

Output:

round() function

In ex_1 above, we defined our ndigits to be None, which is the same as no ndigits at all.

In ex_2, we can see that rounding 2.5 to the nearest integer gives 2 instead of the expected 3.

ndigits

This is because, as seen in the diagram above, 2.5 is at the middle of the two closest integers, which are; 3 and 2

Since there is no closest integer here, Python uses the IEEE 754 standard called banker’s rounding, which rounds to the nearest least significant even digit. So, between 3 and 2, we have 2 as the least significant even digit.

In ex_4, with ndigits of 0, all digits after the decimal point are replaced with a single zero. This happens regardless of how many digits appear after the decimal point. It is the same as passing None or no ndigits at all. The only difference is that with a ndigits of zero, the result returned is float.

In ex_5, we are rounding to a negative decimal place. Since ndigits is -1, it will round the number to the closest multiple of 10-(-1) = 10.

the nearest least significant even digit

From the diagram above, 12.56 is in between the two multiples; 10 and 20, but closest to 10.

In ex_6, the result is not as expected. The expected value is 2.68 instead of 2.67. We shall see later that this is one of the areas where the Decimal module outshines the round() function.

Rounding With Math Module

Over the years, mathematicians have developed an awful lot of rounding methods in order to address the problem of rounding. In this section, we shall go through a few of the commonly used methods.

#1) Truncation

This is the simplest method of rounding numbers. Here, we simply replace every digit after the position we want to keep with zeros.

The math library has the trunc() function that can truncate floating-point values. However, it can only truncate to whole numbers and doesn’t accept an argument for the number of decimal places to keep.

In the example below, we shall customize this function so that it can take into consideration the number of decimal places.

Example 3: Customized truncate() function that takes an argument for decimal places to keep

import math
def truncate(number, decimal=0):
    tens = 10.0

    if not isinstance(decimal, int):
        raise TypeError("Argument 'decimal' must be of type int")

    if decimal == 0:
        return math.trunc(number)
    
    multiples = math.pow(tens, decimal) # tens ** decimal 
    return math.trunc(number * multiples) / multiples

if __name__ == '__main__':
    number = 435.3387743
    decimal_list = [0, 2, 3, 4, -1, -2]
    for decimal in decimal_list:
        print("Truncate {} to {} decimal: {}".format(number, decimal, truncate(number, decimal)))

Output:

Customized truncate()

In summary, if the decimal argument is provided and is valid(not float), we first multiply the number with 10.0n, where n is the decimal. Then, we truncate it using the math.trunc() function. Finally, we divide it with the same 10.0n. The decimal, n can be negative to truncate the digits to the left of the decimal point.

#2) Rounding Up

This technique always rounds a number up to a number of digits greater than or equal to the given number.

The Python math module has the ceil() function that rounds a number to the nearest integer greater than or equal to the given number. This function only takes in one argument. However, just as in example 3, we shall build a customized function that takes in the number of decimal places to keep.

Example 4: Customized rounding_up() function that takes an argument for decimal places to keep

import math
def rounding_up(number, decimal=0):
    tens = 10.0

    if not isinstance(decimal, int):
        raise TypeError("Argument 'decimal' must be of type int")

    if decimal == 0:
        return math.ceil(number)
    
    multiples = math.pow(tens, decimal) # tens ** decimal 
    return math.ceil(number * multiples) / multiples

if __name__ == '__main__':
    numbers = [1.2, 1.534, 33.2, 3356, -1.6]
    decimal_list = [0, 1, -1, -2, 0]
    for decimal, number in zip(decimal_list, numbers):
        print("Round up {} to {} decimal: {}".format(number, decimal, rounding_up(number, decimal)))

Output:

Rounding Up

In the output above, rounding up 1.2 gives 2 because the closest integers to 1.2 are 1 and 2 and 2 happens to be the greatest. A negative decimal input rounds up the number to the left of the decimal point. Rounding up -1.6 to 0 decimal place gives -1 because the closest integers are -1 and –2, and -1 happens to be the greatest.

#3) Rounding Down

This technique always rounds a number down to a number of digits smaller than or equal to the given number.

The Python math module has the floor() function that rounds a number to the nearest integer, smaller than or equal to the given number. Just as ceil(), this function doesn’t allow us to specify the number of decimal places to keep.

Example 5: Customized rounding_down() function that takes an argument for decimal places to keep

import math
def rounding_down(number, decimal=0):
    tens = 10.0

    if not isinstance(decimal, int):
        raise TypeError("Argument 'decimal' must be of type int")

    if decimal == 0:
        return math.floor(number)
    
    multiples = math.pow(tens, decimal) # tens ** decimal 
    return math.floor(number * multiples) / multiples

if __name__ == '__main__':
    numbers = [1.2, 1.534, 33.2, 3356, -1.6]
    decimal_list = [0, 1, -1, -2, 0]
    for decimal, number in zip(decimal_list, numbers):
        print("Round down {} to {} decimal: {}".format(number, decimal, rounding_down(number, decimal)))

Output:

Rounding Down

#4) Rounding Half Up

This strategy is the common method of rounding, which is the same as rounding up In addition, it breaks ties by rounding up. That is, half-way values of x are always rounded up.

In this strategy, one digit is checked to determine the rounding direction. After shifting the decimal point by 10-n as we did for the above examples, we investigate the digit after the shifted decimal point to know if it is less than or greater than or equal to 5.

This is mostly done by adding 0.5 to the shifted value, which will add a 1 to the integer part of the shifted value if greater than 0.5. Then we use the floor() function to get the largest integer.

Example 6: Rounding half up or Round half toward positive infinity.

import math
def rounding_half_up(number, decimal=0):
    tens = 10.0
    half_way = 0.5

    if not isinstance(decimal, int):
        raise TypeError("Argument 'decimal' must be of type int")

    if decimal == 0:
        return math.floor(number + half_way)
    
    multiples = math.pow(tens, decimal) # tens ** decimal 
    return math.floor(number * multiples + half_way) / multiples

if __name__ == '__main__':
    numbers = [7.6, 7.5, 7.4, 1.23, 1.28, 1.25, -1.225]
    decimal_list = [0,0,0,1,1,1,2]
    for decimal, number in zip(decimal_list, numbers):
        print("Round half up {} to {} decimal: {}".format(number, decimal, rounding_half_up(number, decimal)))

Output:

Rounding Half Up

#5) Rounding Half Down

This strategy is the opposite of Rounding Half Up. It breaks ties by rounding to the lesser of the two ties. In short, it makes 0.5 go down.

To achieve this, the modification we have to do to the rounding_half_up() in example 6 is to change the floor() function to ceil(). Then we subtract 0.5 instead of adding.

Example 7: Rounding half down

import math
def rounding_half_down(number, decimal=0):
    tens = 10.0
    half_way = 0.5

    if not isinstance(decimal, int):
        raise TypeError("Argument 'decimal' must be of type int")

    if decimal == 0:
        return math.ceil(number - half_way)
    
    multiples = math.pow(tens, decimal) # tens ** decimal 
    return math.ceil(number * multiples - half_way) / multiples

if __name__ == '__main__':
    numbers = [7.6, 7.5, 7.4, 1.23, 1.28, 1.25, -1.225]
    decimal_list = [0,0,0,1,1,1,2]
    for decimal, number in zip(decimal_list, numbers):
        print("Round half down {} to {} decimal: {}".format(number, decimal, rounding_half_down(number, decimal)))

Output:

Rounding Half Down

#6) Rounding Half To Even

This strategy breaks ties by rounding to the nearest least even number. In short, it rounds 0.5 to the nearest even digit.

This strategy is used by the built-in round() function to break ties and it is the default rounding rule in the IEEE-754 standard. We already explained why rounding 2.5 returned 2 instead of 3 in example 2 above.

Rounding With Decimal Module

The Python Decimal module has several benefits over the round() built-in. It has an exact decimal representation. We saw in example 1, the unpredictability of floating-point numbers, where 0.1 + 0.1 + 0.1 == 0.3 returned False. With the Decimal module, the expected value is returned.

Example 8: Exact decimal representation

>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1')
Decimal('0.3')

Let’s examine the default context of the decimal module by invoking its .getcontext() method.

Example 9: Viewing the current context of the Decimal module

>>> import decimal
>>> decimal.getcontext()
Context(prec=1, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[InvalidOperation, DivisionByZero, Overflow])

We can notice that the default rounding strategy used is rounding half even. Also, the default precision is 26 digits. However, note that these values can be altered to suit our needs.

Rounding a decimal is done with the .quantize() method. This method takes in a decimal value, representing the number of decimal places to keep.

Example 10: Rounding with the Decimal module

>>> from decimal import Decimal
>>> d = Decimal('2.675')
>>> d.quantize(Decimal('1.00')) # round to 2 decimal places
Decimal('2.68')

We can remember in example 2 that rounding 2.675 to 2 decimal places with the round() function failed by returning 2.67 instead of 2.68.

As mentioned earlier, we can alter the values of the precision attribute and rounding strategy to one of the following flags:

Table 1: Flags for rounding strategy

FlagDescription
ROUND_CEILINGRound towards Infinity or rounding up.
ROOUND_DOWNRound towards zero or truncate.
ROUND_FLOORRound towards -Infinity or rounding down.
ROUND_HALF_DOWNRound to nearest with ties going towards zero.
ROUND_HALF_EVENRound to nearest with ties going to nearest even integer.
ROUND_HALF_UPRound to the nearest number and break ties by rounding up.
ROUND_UPRound away from zero.
ROUND_05UPRound up if the last digit is 0 or 5, otherwise round down.

These flags may not mean the same thing as we saw at the beginning of this article. For example, ROUND_CEILING works like our round_up() function while the ROUND_FLOOR works like our round_down() function.

Also, both ROUND_FLOOR and ROUND_CEILING are not symmetric around zero, unlike both ROUND_UP and ROUND_DOWN.

ROUND_DOWN works like our truncate() function by rounding everything towards zero.

Let’s see the first flag in action.

Example 11: Rounding up with decimal.ROUND_CEILING

import decimal

def decimal_ceil(number, dec=0):
    return decimal.Decimal(str(number)).quantize(decimal.Decimal(str(dec)))


if __name__ == '__main__':
    # set rounding strategy
    decimal.getcontext().rounding = decimal.ROUND_CEILING

    numbers = [1.2, 1.534, -1.6]
    decimal_list = [0, 1., 0]
    for dec, number in zip(decimal_list, numbers):
        print("Round up {} to {} decimal: {}".format(number, dec, decimal_ceil(number, dec)))

Output:

Rounding with Decimal Module

Rounding Numpy Arrays

The Numpy library is one of the most commonly used libraries in data science and scientific computing. This library provides many functions that can be used to round float numbers. We are going to look at a few here.

np.round(a[, decimals=0, out=None])

It takes in an array-like input data and an optional number of decimal places to round to (default to 0). Just as we saw with the built-in round() above, this Numpy round() function uses the ROUND HALF TO EVEN strategy and can take in a negative value for the decimal argument.

It is not a built-in library, so needs to be installed. It comes installed with Anaconda, or we can run this command on our terminal to get it installed.

pip3 install numpy

Example 12: Round array of floating-point numbers

import numpy as np
# seed so that the same output can be reproduced
np.random.seed(234)

def round_np(array_data, decimal=0):
    return np.round(array_data, decimal)

if __name__ == '__main__':
    data = np.random.randn(3,3)
    print("Raw data:\n {}".format(data))

    decimals = 3
    result = round_np(data, decimals)
    print("Rounded data to {} decimals:\n {} ".format(decimals, result))

Output:

Rounding Numpy Arrays

The cool thing about this numpy.round() function is that it takes in an array-like data. Unlike the built-in round() function, this function acts on a list or array of numbers at a time.

NB: In order to reproduce the same result above, make sure to seed with 234 as in the code above.

Other Functions

In this section, we shall take a look at a few other functions that Numpy provides for rounding floating-point numbers into the nearest integer;

Table 2: Numpy functions for rounding floating-point numbers to the nearest integer.

Numpy functionDescription
numpy.floor()Rounds every floating-point number in an array to the nearest integer lesser or equal to the original number.
numpy.ceil()Rounds every floating-point number in an array up to the nearest integer greater or equal to the original number
numpy.truct()Rounds every floating-point number in an array to its integer component.
numpy.rint()Rounds every floating-point number to its nearest integer using the ROUNDING HALF TO EVEN strategy

Just as we saw with the built-in math library, we can also customize these functions so that they can round to a specified number of decimal places.

Example 13: Customize the numpy.floor() function to accept an argument for the number of decimal places to round to.

import numpy as np
def rounding_down(array_data, decimal=0):
    tens = 10.0

    if not isinstance(decimal, int):
        raise TypeError("Argument 'decimal' must be of type int")

    if decimal == 0:
        return np.floor(array_data)
    
    multiples = tens ** decimal 
    return np.floor(array_data * multiples) / multiples

if __name__ == '__main__':
    np.random.seed(234)
    data = np.random.randn(3,3)
    decimals = 3
    print("Original Data: {}".format(data))
    print("Round Down to {} decimals:\n {}".format(decimals, rounding_down(data, decimals)))

Output:

Round Down new

Rounding Pandas Series and DataFrame

The Pandas library is also one of the most commonly used libraries in data science and data analysis. Just as Numpy, It also comes installed with Anaconda or we can run the command below to install it.

pip3 install pandas

Pandas have the DataFrame and Series as their data structures and both have the .round() function that works exactly like np.round() we saw above.

Example 14: Rounding with Pandas

In this example, we shall see how to round Series and DataFrame objects, also, we shall see how to use numpy’s rounding functions on Pandas Series and DataFrame objects.

import pandas as pd 
import numpy as np 

np.random.seed(123)

series = pd.Series(np.random.rand(3))
df = pd.DataFrame(np.random.randn(3,4), columns=['A','B','C','D'])

print("Original Series:\n {}".format(series))
print("Original DF:\n {}".format(df))

print("\n")
# Use .round() and round all data the same on Dataframe and Series
decimals = 3
series_result = series.round(decimals)
df_result = df.round(decimals)

print("Series rounded to {} decimal:\n {}".format(decimals,series_result))
print("DF rounded to {} decimal:\n {}".format(decimals,df_result))

print("\n")
# Use .round() and specify column-by-column precision on DataFrame
decimals = {"A":2, "B":3, "C":4, "D":5}
df_result = df.round(decimals)
print("DF rounded to {} decimal:\n {}".format(decimals,df_result))

print("\n")
# Use numpy's rounding functions on DataFrame
print("DF rounded with np.floor:\n {}".format(np.floor(df)))
print("DF rounded with np.ceil:\n {}".format(np.ceil(df)))

Output:

Rounding Pandas Series and DataFrame

Frequently Asked Questions

Q #1) How do you round a number in a list Python?

Answer: We have many ways to round numbers in a list in Python. The first will be to use a list comprehension together with any rounding function like below:

>>> a =[1.234, 2.345, 3.45, 1.45]
>>> [round(i,1) for i in a] # for each number, round with round() function
[1.2, 2.3, 3.5, 1.4]

In this case, the most common way will be to use the numpy.round() method:

>>> import numpy as np
>>> a = [1.234, 2.345, 3.45, 1.45]
>>> np_a = np.array(a) # convert list to numpy array
>>> np_a_round = np.round(np_a, 1) # round to 1 decimal place
>>> list(np_a_round) # convert numpy array back to list.
[1.2, 2.3, 3.4, 1.4]

Q #2) What is round() in Python

Answer: The round() is a built-in function in Python that takes in two arguments; the number to round(number) and the number of decimal places to keep(ndigits), then returns the closest multiple of 10-ndigits but breaks ties by applying the ROUNDING HALF TO EVEN strategy, which rounds to the nearest least even number.

Q #3) How do you round to 5 decimal places in Python?

Answer: With the built-in round() function, we can round to any decimal place by providing the number of decimal places to keep as the second argument. Say we want to round 3.45610688 to 5 decimal places.

>>> number = 3.45610688
>>> round(number, 5)
3.45611

Q #4) How do you round off without using the round function in Python?

Answer: To round up or down without the built-in round(), we first multiply the number to be rounded by 10.0n, where n is the decimal place to keep. Then, we use math.trunc() for truncation, math.ceil() for rounding up or math.floor() for rounding down. Finally, we divide the result by the same 10.0n.

Below is an example for rounding up:

multiples = math.pow(number, n) 
math.ceil(number * multiples) / multiples

Q #5) How do you round to the nearest whole number?

Answer: To round to the nearest whole number, we first shift the decimal point n times where n is the number of decimal places to round. Then we check the first digit after the decimal point.

If it is less than 5, we do nothing, but if it is greater or equal to 5, we round up the digit before the decimal point by adding 1 to it. Finally, we shift the decimal point back to its original position.

Conclusion

In this article, we looked at what rounding numbers are and they apply to floating-point numbers. We examined the Python built-in round() function and also looked at rounding with the math module where we saw various rounding strategies like Truncation, Rounding Up, Rounding Down, and more.

We also covered rounding with the Decimal module and finally, we looked at rounding Numpy arrays and Pandas Series and DataFrames.

=> Read Through ALL Python Tutorials Here

Was this helpful?

Thanks for your feedback!

Leave a Comment