Why does round(1/2) returns 0 but round(7/2) returns 4 in Python?

Question:

Hello I was trying to determine if a element of a given list was part of the second half of the list. For example if it had 7 elements

my_list = ['a','b','c','d','e','f','g']

my function would only return True for elements ‘e’, ‘f’ and ‘g’.

The solution was pretty simple:

def fashionably_late(arrivals, name):
    
    return (arrivals.index(name) + 1 > round(len(arrivals)/2))

My solution had a bug though because of how rounding works, I just want to understand why

However this behavior from python is perplexing, why is 7/2 (i.e 3.5 ) rounded to 4 the same way as 9/2 (i.e. 4.5) to 4.

for i in range(1,11):
    print(i , round(i/2))
​

    1 0
    2 1
    3 2
    4 2
    5 2
    6 3
    7 4
    8 4
    9 4
    10 5

#The way I expect it to work is: 

for i in range(1,11):
    print(i , i//2 + i %2)
​
1 1
2 1
3 2
4 2
5 3
6 3
7 4
8 4
9 5
10 5

As a side note I am aware of the math.ceil() function.

Asked By: Michel G

||

Answers:

Check out How to properly round-up half float numbers?.

The issue is caused by the fact that rounding precisely 0.5 is slightly arbitrary in math and there are differing opinions on how to do it. Python follows the strategy that prefers to round to even numbers.

Answered By: David_Leber

From the docs

if two multiples are equally close, rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2)

This convention is often called statistician’s rounding, as it produces more accurate results over large aggregate data (always rounding up on 0.5 increases the average of the data by 0.5, but rounding either way using a consistent rule keeps the mean more consistent, on aggregate)

Answered By: Silvio Mayolo

Well, it’s weired to me as well, but according to here

Round() function in Python follows the half to even rounding strategy.
In this strategy, the number is rounded off to its nearest even
integer. For example, if we need to round off 7.5, it will be rounded
off to its nearest even integer that is 8. And 4.5 will be rounded off to its nearest EVEN integer thus 4.

You may try:

for i in range(10):
    print(f"{i}/2={i/2} => round{i/2}={round(i/2)}")

and you will see:

0/2=0.0 => round(0.0)=0
1/2=0.5 => round(0.5)=0
2/2=1.0 => round(1.0)=1
3/2=1.5 => round(1.5)=2
4/2=2.0 => round(2.0)=2
5/2=2.5 => round(2.5)=2
6/2=3.0 => round(3.0)=3
7/2=3.5 => round(3.5)=4
8/2=4.0 => round(4.0)=4
9/2=4.5 => round(4.5)=4

For your purpose, you may add a small number epsilon=1e-6 to change the round() behaviour toward to your need:

epsilon=1e-6

for i in range(10):
    print(f"round({i/2}+epsilon)={round(epsilon+i/2)}")

Then you will have:

round(0.0+epsilon)=0
round(0.5+epsilon)=1
round(1.0+epsilon)=1
round(1.5+epsilon)=2
round(2.0+epsilon)=2
round(2.5+epsilon)=3
round(3.0+epsilon)=3
round(3.5+epsilon)=4
round(4.0+epsilon)=4
round(4.5+epsilon)=5

Enjoy!

Answered By: Paul Wang
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.