It appears that the default Python round(1 / 2)
gives 0.
How to round float 0.5 up to 1.0, while still rounding 0.45 to 0.0, as the usual school rounding?
It appears that the default Python round(1 / 2)
gives 0.
How to round float 0.5 up to 1.0, while still rounding 0.45 to 0.0, as the usual school rounding?
It is actually currently considered proper to not blindly round *.5 up. Rather, it is proper to round *.5 to the nearest even number. Python 3 implements this "proper" form of "banker rounding", but a lot of other languages don't (yet). Blindly rounding *.5 up produces a slight bias, but "banker rounding" helps to balance it it out. See this thread for more info. So...
Method 1
You could conditionally use a ceil(...)
function (from the math module) for the rounding up aspect. You'll have to do it conditionally in order to also maintain the regular rounding behavior for values less than 0.5. Try something like the following (note that this isn't extremely robust in that it only works on positive values...it should be able to be easily adapted to work with both positive and negative values though):
import math
val = 1.5
x = 0
if (float(val) % 1) >= 0.5:
x = math.ceil(val)
else:
x = round(val)
Note that a ceil(...)
function will return an integer, not a float. This shouldn't be a major issue, but now you are aware.
Method 2
From the post I linked to above, it looks like another option is to use the decimal module to emulate the "old" way of rounding's behavior. I'm kind of copy & pasting from there, but here you go:
import decimal
x = decimal.Decimal('1.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_UP)
Supposedly the decimal.ROUND_HALF_UP
form of rounding is what you are looking for. This way you don't have to use a ceil(...)
function conditionally.
Getting the "school" rounding, with rounding away from 0 for value in between, also for negative numbers, the function below can be used. This is also the rounding that was in Python 2.
def round_school(x):
i, f = divmod(x, 1)
return int(i + ((f >= 0.5) if (x > 0) else (f > 0.5)))
Some example results:
1.50: 2
1.49: 1
0.50: 1
0.49: 0
-0.49: 0
-0.50: -1
-1.49: -1
-1.50: -2
decimal
from decimal import Decimal, ROUND_HALF_UP
def round(number, ndigits=None):
"""Always round off"""
exp = Decimal('1.{}'.format(ndigits * '0')) if ndigits else Decimal('1')
return type(number)(Decimal(number).quantize(exp, ROUND_HALF_UP))
print(round(4.115, 2), type(round(4.115, 2)))
print(round(4.116, 2), type(round(4.116, 2)))
print(round(4.125, 2), type(round(4.125, 2)))
print(round(4.126, 2), type(round(4.126, 2)))
print(round(2.5), type(round(2.5)))
print(round(3.5), type(round(3.5)))
print(round(5), type(round(5)))
print(round(6), type(round(6)))
# 4.12 <class 'float'>
# 4.12 <class 'float'>
# 4.13 <class 'float'>
# 4.13 <class 'float'>
# 3.0 <class 'float'>
# 4.0 <class 'float'>
# 5 <class 'int'>
# 6 <class 'int'>
math
import math
def round(number, ndigits=0):
"""Always round off"""
exp = number * 10 ** ndigits
if abs(exp) - abs(math.floor(exp)) < 0.5:
return type(number)(math.floor(exp) / 10 ** ndigits)
return type(number)(math.ceil(exp) / 10 ** ndigits)
print(round(4.115, 2), type(round(4.115, 2)))
print(round(4.116, 2), type(round(4.116, 2)))
print(round(4.125, 2), type(round(4.125, 2)))
print(round(4.126, 2), type(round(4.126, 2)))
print(round(2.5), type(round(2.5)))
print(round(3.5), type(round(3.5)))
print(round(5), type(round(5)))
print(round(6), type(round(6)))
# 4.12 <class 'float'>
# 4.12 <class 'float'>
# 4.13 <class 'float'>
# 4.13 <class 'float'>
# 3.0 <class 'float'>
# 4.0 <class 'float'>
# 5 <class 'int'>
# 6 <class 'int'>
import math
from timeit import timeit
from decimal import Decimal, ROUND_HALF_UP
def round1(number, ndigits=None):
exp = Decimal('1.{}'.format(ndigits * '0')) if ndigits else Decimal('1')
return type(number)(Decimal(number).quantize(exp, ROUND_HALF_UP))
def round2(number, ndigits=0):
exp = number * 10 ** ndigits
if abs(exp) - abs(math.floor(exp)) < 0.5:
return type(number)(math.floor(exp) / 10 ** ndigits)
return type(number)(math.ceil(exp) / 10 ** ndigits)
print(timeit('round1(123456789.1223456789, 5)', globals=globals()))
print(timeit('round2(123456789.1223456789, 5)', globals=globals()))
# 1.9912803000000001
# 1.2140076999999998
The math
one is faster.