>>> a = 0.3135
>>> print("%.3f" % a)
0.314
>>> a = 0.3125
>>> print("%.3f" % a)
0.312
>>>
I am expecting 0.313 instead of 0.312 Any thought on why is this, and is there alternative way I can use to get 0.313?
Thanks
Python 3 rounds according to the IEEE 754 standard, using a round-to-even approach.
If you want to round in a different way then simply implement it by hand:
import math
def my_round(n, ndigits):
part = n * 10 ** ndigits
delta = part - int(part)
# always round "away from 0"
if delta >= 0.5 or -0.5 < delta <= 0:
part = math.ceil(part)
else:
part = math.floor(part)
return part / (10 ** ndigits) if ndigits >= 0 else part * 10 ** abs(ndigits)
Example usage:
In [12]: my_round(0.3125, 3)
Out[12]: 0.313
Note: in python2 rounding is always away from zero, while in python3 it rounds to even. (see, for example, the difference in the documentation for the round function between 2.7 and 3.3).
ndigits >= 0. Anyway the issue is that you are hitting float limits. If you check the code you end up on the return line of my_round with 4 / (10 ** (-9)) which should return the correct result but it doesn't. You can easily check for the sign of ndigits and use part * 10 ** abs(ndigits) for the negative cases and this seems to work better.If you need accuracy don't use float, use Decimal
>>> from decimal import *
>>> d = Decimal('0.3125')
>>> getcontext().rounding = ROUND_UP
>>> round(d, 3)
Decimal('0.313')
or even Fraction
Since round() doesn't work correctly, I used the LUA approach instead:
from math import floor
rnd = lambda v, p=0: floor(v*(10**p)+.5)/(10**p)
Testing:
>>> round( 128.25, 1 )
128.2
>>> rnd( 128.25, 1 )
128.3
>>>
To not conflate with other answers
Other tests include:
>>> rnd = lambda v, p=0: round(v*(10**p))/(10**p) # hairetdin's answer
>>> rnd( 128.25, 1 )
128.2
>>> print( '%.3f' % round(128.25,1) ) # Tushar's answer
128.200
Other details:
The duplicate processing of 10**p is negligible in performance from storing and referencing a variable.
Here's an implementation of away-from-zero.
Works for both positive and negative numbers.
Not optimized for performance.
Matches a few error behaviors of the built-in implementation. Output will be int if ndigits is not given, None or 0
import math
def round(number, ndigits=None):
if not hasattr(number, '__round__'):
raise TypeError(f'type {type(number)} doesn\'t define __round__ method')
if not (ndigits==None or type(ndigits)==int)):
raise TypeError(f'{type(ndigits)} object cannot be interpreted as an integer')
if ndigits in [None, 0]:
x = math.floor(.5+abs(number))
y = int(math.copysign(x, number))
else:
m = 10**ndigits
x = math.floor(.5+abs(number)*m)
y = math.copysign(x/m, number)
return y
# end
Define your own language about "the standard" and decide whichever from the multiple IEEE definitions should be implemented by default. This won't change my expectation that the output of round(6.5) should be 7. The GNU C library implements all variants and lets the user of the language to pick. I can't believe Python has chosen to simply pass through whatever is implemented by the underlying library and says no more.
IEEE 754andround to even. The rounding is not wrong. It conforms to the standard.