Rounding numbers in Python is an essential task, especially when dealing with data precision. Python’s built-in round() function uses the rounding half to even strategy, which rounds numbers like 2.5 to 2 and 3.5 to 4. This method helps minimize rounding bias in datasets. To round numbers to specific decimal places, you can use the round() function with a second argument specifying the number of decimals.
For more advanced rounding strategies, you can explore Python’s decimal module or use NumPy and pandas for data science applications. NumPy arrays and pandas DataFrames offer methods for rounding numbers efficiently. In NumPy, you can use functions like np.round(), np.ceil(), np.floor(), and np.trunc() to apply different rounding strategies. For pandas, the df.round() method allows rounding of entire DataFrames or specific columns.
By the end of this tutorial, you’ll understand that:
- Python uses the rounding half to even strategy, where ties round to the nearest even number.
- Python’s default rounding strategy minimizes rounding bias in large datasets.
- You can round numbers to specific decimal places using Python’s
round()function with a second argument. - Different rounding strategies can be applied using Python’s
decimalmodule or custom functions for precision control. - NumPy and pandas provide methods for rounding numbers in arrays and DataFrames, offering flexibility in data manipulation.
You won’t get a treatise on numeric precision in computing, although you’ll touch briefly on the subject. Only a familiarity with the fundamentals of Python is necessary, and the math should feel familiar if you’ve had high school algebra.
You’ll start by looking at Python’s built-in rounding mechanism.
Take the Quiz: Test your knowledge with our interactive “Rounding Numbers in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:
Get Your Code: Click here to download the free sample code you’ll use to learn about rounding numbers in Python.
Python’s Built-in round() Function
Python has a built-in round() function that takes two numeric arguments, n and ndigits, and returns the number n rounded to ndigits. The ndigits argument defaults to zero, so leaving it out results in a number rounded to an integer. As you’ll see, round() may not work quite as you expect.
The way most people are taught to round a number goes something like this:
-
Round the number
ntopdecimal places by first shifting the decimal point innbypplaces. To do that, multiplynby 10ᵖ (10 raised to theppower) to get a new number,m. -
Then look at the digit
din the first decimal place ofm. Ifdis less than 5, roundmdown to the nearest integer. Otherwise, roundmup. -
Finally, shift the decimal point back
pplaces by dividingmby 10ᵖ.
It’s an algorithm! For example, the number 2.5 rounded to the nearest whole number is 3. The number 1.64 rounded to one decimal place is 1.6.
Now open up an interpreter session and round 2.5 to the nearest whole number using Python’s built-in round() function:
>>> round(2.5)
2
Gasp!
Check out how round() handles the number 1.5:
>>> round(1.5)
2
So, round() rounds 1.5 up to 2, and 2.5 down to 2!
Before you go raising an issue on the Python bug tracker, rest assured you that round(2.5) is supposed to return 2. There’s a good reason why round() behaves the way it does.
In this tutorial, you’ll learn that there are more ways to round a number than you might expect, each with unique advantages and disadvantages. round() behaves according to a particular rounding strategy—which may or may not be the one you need for a given situation.
You might be wondering, Can the way I round numbers really have that much of an impact? Next up, take a look at just how extreme the effects of rounding can be.
How Much Impact Can Rounding Have?
Suppose you have an incredibly lucky day and find $100 on the ground. Rather than spending all your money at once, you decide to play it smart and invest your money by buying some shares of different stocks.
The value of a stock depends on supply and demand. The more people there are who want to buy a stock, the more value that stock has, and vice versa. In high-volume stock markets, the value of a particular stock can fluctuate on a second-by-second basis.
Now run a little experiment. Pretend the overall value of the stocks that you purchased fluctuates by some small random number each second, say between $0.05 and -$0.05. This fluctuation may not necessarily be a nice value with only two decimal places. For example, the overall value may increase by $0.031286 one second and decrease the next second by $0.028476.
You don’t want to keep track of your value to the fifth or sixth decimal place, so you decide to chop everything off after the third decimal place. In rounding jargon, this is called truncating the number to the third decimal place. There’s some error to be expected here, but by keeping three decimal places, this error couldn’t be substantial. Right?
To run your experiment using Python, you start by writing a truncate() function that truncates a number to three decimal places:
>>> def truncate(n):
... return int(n * 1000) / 1000
...
The truncate() function first shifts the decimal point in the number n three places to the right by multiplying n by 1000. You get the integer part of this new number with int(). Finally, you shift the decimal point three places back to the left by dividing n by 1000.
Next, you define the initial parameters of the simulation. You’ll need two variables: one to keep track of the actual value of your stocks after the simulation is complete and one for the value of your stocks after you’ve been truncating to three decimal places at each step.
Start by initializing these variables to 100:
>>> actual_value, truncated_value = 100, 100
Now run the simulation for 1,000,000 seconds (approximately 11.5 days). For each second, generate a random value between -0.05 and 0.05 with the uniform() function in the random module, and then update actual and truncated:
>>> import random
>>> random.seed(100)
>>> for _ in range(1_000_000):
... delta = random.uniform(-0.05, 0.05)
... actual_value = actual_value + delta
... truncated_value = truncate(truncated_value + delta)
...
>>> actual_value
96.45273913513529
>>> truncated_value
0.239
The meat of the simulation takes place in the for loop, which loops over the range of numbers between 0 and 999,999. The value taken from range() at each step is stored in the variable _, which you use here because you don’t actually need this value inside of the loop.
At each step of the loop, random.uniform() generates a new random number between -0.05 and 0.05, which you assign to the variable delta. You calculate the new value of your investment by adding delta to actual_value, and then you calculate the truncated total by adding delta to truncated_value and then truncating this value with truncate().
As you can see by inspecting the actual_value variable after running the loop, you only lost about $3.55. However, if you’d been looking at truncated_value, you’d have thought that you’d lost almost all of your money!
Note: In the above example, you use the random.seed() function to seed the pseudo-random number generator so that you can reproduce the output shown here.
To learn more about randomness in Python, check out Generating Random Data in Python (Guide) and Using the NumPy Random Number Generator.
Ignoring for the moment that round() doesn’t behave quite as you expect, go ahead and try rerunning the simulation. You’ll use round() this time to round to three decimal places at each step, and you’ll seed the simulation again to get the same results as before:
>>> random.seed(100)
>>> actual_value, rounded_value = 100, 100
>>> for _ in range(1_000_000):
... delta = random.uniform(-0.05, 0.05)
... actual_value = actual_value + delta
... rounded_value = round(rounded_value + delta, 3)
...
>>> actual_value
96.45273913513529
>>> rounded_value
96.258
What a difference!
Shocking as it may seem, this exact error caused quite a stir in the early 1980s when the system designed for recording the value of the Vancouver Stock Exchange truncated the overall index value to three decimal places instead of rounding. Rounding errors have swayed elections and even resulted in the loss of life.
How you round numbers is important, and as a responsible developer and software designer, you need to know what the common issues are and how to deal with them. So it’s time to dive in and investigate what the different rounding methods are and how you can implement each one in pure Python.
Basic but Biased Rounding Strategies
There’s a plethora of rounding strategies, each with advantages and disadvantages. In this section, you’ll learn about some of the simplest techniques and see how they can influence your data. Later, you’ll learn about better methods.
Truncating
The simplest, albeit crudest, method for rounding a number is to truncate the number to a given number of digits. When you truncate a number, you replace each digit after a given position with 0. Here are some examples: