round builtin inaccuracies

A forum for general discussion of the Python programming language.

round builtin inaccuracies

Postby Mekire » Tue May 07, 2013 2:40 pm

The other day I was reading a post where someone had to write a function to emulate the round builtin. They (as well as someone assisting them) both ignored the fact that the function was supposed to take an optional precision argument. I decided to see how well I could do performance wise vs the builtin.

Well... as expected, pretty dismally. But after I wrote mine I started testing it to confirm it corresponded to the builtin.

Mine is as follows:
Code: Select all
def my_round(number,precision=0):
    scale = 10**precision
    big = number*scale
    big_i = int(big)
    round_big = big_i+int(2*(big-big_i))
    return round_big/float(scale)
And a little test:
Code: Select all
if __name__ == "__main__":
    tests = 10000
    for i in range(tests):
        num = randint(0,10000)+random()
        prec = randint(-10,10)
        builtin,mine = round(num,prec),my_round(num,prec)
        results = "Test {} failed for {:.{p1}f},{p}: builtin = {:.{p}f}, mine = {:.{p}f} "
        assert builtin==mine,results.format(i,num,builtin,mine,p=prec,p1=prec+1)

For the most part it performs identically (result wise), but occasionally it differs; so I investigated these exceptions. And well, it seems that for those cases when the results differed, it was mine that was correct. The built in round doesn't always handle decimals that end in a 5 consistently. And neither does my version for that matter.

So the reason for this is obviously floating point precision; but what is a good solution? It seems like a function that can't consistently round things that are as simple as:
Code: Select all
>>> round(0.285,2)
>>> round(0.385,2)
isn't extremely helpful.

I have occasionally run into these floating point precision issues and I never really know the best solution. I suppose one can always use a tolerance inequality to force the machine to admit that something like 0.9999999999999999 is really 1 but this seems like such a pain.

New Users, Read This
  • Use code tags when posting code.
  • Include any errors with your post (in code tags).
  • Describe your problem; not your chosen solution.
  • Make examples the minimum length to demonstrate your issue.
User avatar
Posts: 1711
Joined: Thu Feb 07, 2013 11:33 pm
Location: Tucson, Arizona

Re: round builtin inaccuracies

Postby Yoriz » Tue May 07, 2013 2:45 pm

Due to the reasons discussed here we will be moving to on October 1 2016
This forum will be locked down and no one will be able to post/edit/create threads, etc. here from thereafter. Please create an account at the new site to continue discussion.
User avatar
Posts: 1672
Joined: Fri Feb 08, 2013 1:35 am
Location: UK

Re: round builtin inaccuracies

Postby setrofim » Tue May 07, 2013 2:54 pm

Using Decimal is the most common way to get around that in Python. Though that does involve a significant preformance hit, so you wouldn't want to use it, e.g., for 3D graphics. Another way is to avoid floating point entirely. For example, if you're working with highly precise timings (e.g. you're preformance testing an embedded system), and it is important that you record the exact value of 1.234567901 seconds, you can instead store it as 12345678901 nano seconds and then use integer arithmetic (including division) throughout your application. If you are stuck with using native floating point types, then considering values whos absolute difference is smaller than the machine epsilon as being equal is the the only way around this, as it is essentially a hardware limitation.
Posts: 288
Joined: Mon Mar 04, 2013 7:52 pm

Return to General Discussions

Who is online

Users browsing this forum: No registered users and 5 guests