-->

Change in Python built in round() function between

2019-05-05 18:50发布

问题:

Has the built in round() function in Python changed between 2.4 and 2.7?

Python 2.4:

Python 2.4.6 (#1, Feb 12 2009, 14:52:44)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = 1480.39499999999998181010596454143524169921875
>>> round(f,2)
1480.4000000000001
>>>

Python 2.7:

Python 2.7.1 (r271:86832, May 13 2011, 08:14:41)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = 1480.39499999999998181010596454143524169921875
>>> round(f, 2)
1480.39
>>>

Is there anyway to get the Python 2.4 behaviour back?

I'm aware that the right answer is of course to use the decimal arithmetic module. Unfortunately, this probably isn't an option at this point given time limitations.

Update

For context i'm comparing values in two systems, one of which uses a decimal representation and the other a floating point one. This may (or may not) be a legitimate difference between the systems which needs to be checked further, so i'll consult users and deal with it at the "reporting" level, rather than at the point I get the data from the systems.

Thanks for your help!

回答1:

The answer to your first question is: yes, round was fixed in Python 2.7.

The answer to your second question is: I'm not sure, short of actually using an earlier Python (2.6 should still exhibit 2.4's behavior). In this particular case, your value is indistinguishable from 1480.395, and 1480.395 (to us base-10 humans) rounds to 1480.40. So I suppose you could try first rounding to one place beyond what you are really after, turn that into a string, then get a Decimal from that... but sorry, I can't think of anything that doesn't involve Decimal. If I think of something (before someone else posts something better), I'll come back and edit.

(But really, is Decimal all that hard to use?)

Edit: Here is an example of how to use Decimal:

>>> from decimal import Decimal
>>> f = Decimal('1480.395')
>>> f.quantize(Decimal('0.00'))
Decimal('1480.40')
>>> float(Decimal('1480.40'))
1480.4

Some notes:

Python 2.7 allows you to use floats as input to Decimal. Don't do this as you would be back where you started. Also, don't use the built-in round function on a Decimal, because that function is going to convert your Decimal right back to float, and thus you would be back where you started. In its simplest form, Decimal's quantize takes another Decimal that has the number of decimal places you really want (think of it as a template for the rounding). If your data absolutely has to be float, then only convert back to float after all your Decimal calculations are done.

Finally: I'm not sure this covers all the possible weird corner cases in Python 2.4's float behavior. If you are relying on exactly Python 2.4's behavior, there may be no substitute to running on Python 2.4. What I have described above is merely one step closer to human-style rounding.



回答2:

Probably not since Python 2.4 actually returned an incorrect result. Since you are rounding away an amount that is strictly less than 5, the correct result should be rounded down.

If you can describe the exact behaviour you need, we may be able to create an alternative approach.



回答3:

In the what's new in python 2.6 page I see the following (with reference to PEP 3141):

Python 3.0 adds several abstract base classes for numeric types inspired by Scheme’s numeric tower. These classes were backported to 2.6 as the numbers module.

...

In Python 3.0, the PEP slightly redefines the existing builtins round(), math.floor(), math.ceil(), and adds a new one, math.trunc(), that’s been backported to Python 2.6. math.trunc() rounds toward zero, returning the closest Integral that’s between the function’s argument and zero.

And, thanks to @mark-dickinson comment, in the what's new in python 2.7 page, the following text is found:

The round() function is also now correctly rounded.

So yes, the change happened in python 2.7. This can be verified checking that in python 2.6 the behaviour is still the old one:

Python 2.6.7 (r267:88850, Aug 11 2011, 12:18:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = 1480.39499999999998181010596454143524169921875
>>> round(f, 2)
1480.4000000000001

In my linux distribution I still have access to both python 2.6 and python 2.7 and probably you have also in yours; so, if you want the old behaviour, maybe you can try to run the code in python 2.6.