954,557 Members — Technology Publication meets Social Media
Username:
Password:
Lost login information?
Have something to say? Contribute New Article Reply to this Article

Round to nearest integer.

How would you safely round a floating point number to the nearest integer ? Python has the built in function round , but it returns a floating point number

>>> round(4.9)
5.0
>>> help(round)
Help on built-in function round in module __builtin__:

round(...)
    round(number[, ndigits]) -> floating point number
    
    Round a number to a given precision in decimal digits (default 0 digits).
    This always returns a floating point number.  Precision may be negative.

Using int(round(x)) to obtain the nearest integer looks like a bad idea because the int() function is discontinuous near integer values, and therefore, round(x) returns a value wich is very close to a point of discontinuity of int :

>>> int(4.999999999999999)
4
>>> int(4.99999999999999999999)
5

I designed the following function, which is guaranteed to be stable (except near half integers, the natural points of discontinuity of the function 'nearest integer'):

def iround(x):
    """iround(number) -> integer
    Round a number to the nearest integer."""
    return int(round(x) - .5) + (x > 0)

Here are a few calls

>>> iround(4.9)
5
>>> iround(5.1)
5
>>> iround(-4.9)
-5
>>> iround(-5.1)
-5

Now, it turns out that for every value that I tried on my computer, iround returns exactly the same value as int(round(x)) . To see it I wrote a function for the difference

def diff(x):
    return int(round(x)) - iround(x)

My question is: does the diff function above always return 0 for every floating value of x on any computer, and is there something in the implementation of the builtin round function which guarantees stability for int(round(x)) ? This does not seem to be documented in the official doc. What do you think of this ?

Gribouillis
Posting Maven
Moderator
2,786 posts since Jul 2008
Reputation Points: 1,044
Solved Threads: 691
 

Sorry, there was a bug in the iround function above. It should be

def iround(x):
    """iround(number) -> integer
    Round a number to the nearest integer."""
    y = round(x) - .5
    return int(y) + (y > 0)

The previous version does not work near 0.

Gribouillis
Posting Maven
Moderator
2,786 posts since Jul 2008
Reputation Points: 1,044
Solved Threads: 691
 

Perhaps I am missing something, but the following code should work. But once you get past the precision of floating point numbers you will have to use the decimal module whichever way you do it.

import decimal
x = decimal.Decimal("2.4999999999999999999999999")
whole, remain = divmod(x, 1)
if remain >= decimal.Decimal("0.5"):
    whole += 1
print whole
woooee
Nearly a Posting Maven
2,454 posts since Dec 2006
Reputation Points: 777
Solved Threads: 714
 

Perhaps I am missing something, but the following code should work. But once you get past the precision of floating point numbers you will have to use the decimal module whichever way you do it.

import decimal
x = decimal.Decimal("2.4999999999999999999999999")
whole, remain = divmod(x, 1)
if remain >= decimal.Decimal("0.5"):
    whole += 1
print whole


Yes, I think you are definitely missing something. The question is not to go past the precision of floating point numbers, the question is stability and portability. For example, if I want the integer nearest to 3.1, I don't need much precision. The result is 3 and it is astable and portable result. Stable means that if I change 3.1 by adding or substracting a small perturbation, say 0.0001, the result is still 3 (unstability should occur only near half integers, like 3.5). Portable means that the result should be 3 on any computer.

Now, the expression int(round(3.1)) could be unstable or non portable, because round(3.1) returns the floating number 3.0 and usually, floating point numbers are not exact in machine representation (but note that integers like 3.0 can possibly be represented exactly). Theint function is unstable near the point 3.0 because int(3.0 - 1e-10) == 2 and int(3.0 + 1e-10) == 3 . So I cannot guarantee that int(round(3.1)) yields 3 in any circumstances, and not 2 on certain computers or OSes. As far as I know, this is not documented in python.

Using decimal numbers here is not a good idea because it will be much slower than using ordinary floating point numbers and the problem does not require more precision than the usual float precision.

Gribouillis
Posting Maven
Moderator
2,786 posts since Jul 2008
Reputation Points: 1,044
Solved Threads: 691
 

I might also be missing something but this should work:

def round(num):
	if (num > 0):
		return int(num+.5)
	else:
		return int(num-.5)
nickles
Newbie Poster
11 posts since Apr 2010
Reputation Points: 23
Solved Threads: 1
 
My question is: does the diff function above always return 0 for every floating value of x on any computer, and is there something in the implementation of the builtin round function which guarantees stability for int(round(x)) ? This does not seem to be documented in the official doc. What do you think of this ?


If it's not documented in the official documentation, use your function and forsake int+round. In fact, if you're not sure, or you think somebody reading your code later might not be sure, just go ahead and use your function. What's the harm? Performance? You're using Python.

AuburnMathTutor
Junior Poster
125 posts since Jun 2010
Reputation Points: 48
Solved Threads: 12
 

I also think that the action of using int function or as hidden by round, is disruptive as it is very discontinuous by its very nature. Sometimes it is better to stay with integers and be clearly discontinuous. Same could be done, maybe by multiplying numbers by some constant before use and dividing by the factor in the end even with floating point numbers.

Where the need usually arise is for printing neat results. Is there int embedded in numeric algorithm inside or is it only on this interface "surface"? If it is inside, maybe there could be some kind of sanity check filter for disruptive "imposible" data points and do recalculation to slightly tweaked parameter values to get them into the general line. There is though the real chaotic nature of things and we should not wipe it under the carpet.

pyTony
pyMod
Moderator
5,359 posts since Apr 2010
Reputation Points: 782
Solved Threads: 852
 

Thank you for all your answers. It seems that the more secure way is to use an auxiliary function. I'll stay with nickles' solution, or the iround function I wrote above.
There once was a math.rint function (in python 1.6), but it was removed by G Van Rossum:After a brief consult with Tim, I've decided to drop math.rint() -- it's not standard C, can't be implemented in portable C, and its naive (non-IEEE-754-aware) effect can easily be had in other ways.
See this link http://markmail.org/message/4di24iqm7zhc4rwc .

Gribouillis
Posting Maven
Moderator
2,786 posts since Jul 2008
Reputation Points: 1,044
Solved Threads: 691
 

This question has already been solved

Post: Markdown Syntax: Formatting Help
You
View similar articles that have also been tagged: