How to properly round-up half float numbers?
Solution 1
The Numeric Types section documents this behaviour explicitly:
round(x[, n])
x rounded to n digits, rounding half to even. If n is omitted, it defaults to 0.
Note the rounding half to even. This is also called bankers rounding; instead of always rounding up or down (compounding rounding errors), by rounding to the nearest even number you average out rounding errors.
If you need more control over the rounding behaviour, use the decimal
module, which lets you specify exactly what rounding strategy should be used.
For example, to round up from half:
>>> from decimal import localcontext, Decimal, ROUND_HALF_UP
>>> with localcontext() as ctx:
... ctx.rounding = ROUND_HALF_UP
... for i in range(1, 15, 2):
... n = Decimal(i) / 2
... print(n, '=>', n.to_integral_value())
...
0.5 => 1
1.5 => 2
2.5 => 3
3.5 => 4
4.5 => 5
5.5 => 6
6.5 => 7
Solution 2
For example:
from decimal import Decimal, ROUND_HALF_UP
Decimal(1.5).quantize(0, ROUND_HALF_UP)
# This also works for rounding to the integer part:
Decimal(1.5).to_integral_value(rounding=ROUND_HALF_UP)
Solution 3
You can use this:
import math
def normal_round(n):
if n - math.floor(n) < 0.5:
return math.floor(n)
return math.ceil(n)
It will round number up or down properly.
Solution 4
round()
will round either up or down, depending on if the number is even or odd. A simple way to only round up is:
int(num + 0.5)
If you want this to work properly for negative numbers use:
((num > 0) - (num < 0)) * int(abs(num) + 0.5)
Note, this can mess up for large numbers or really precise numbers like 5000000000000001.0
and 0.49999999999999994
.
Solution 5
The behavior you are seeing is typical IEEE 754 rounding behavior. If it has to choose between two numbers that are equally different from the input, it always picks the even one. The advantage of this behavior is that the average rounding effect is zero - equally many numbers round up and down. If you round the half way numbers in a consistent direction the rounding will affect the expected value.
The behavior you are seeing is correct if the objective is fair rounding, but that is not always what is needed.
One trick to get the type of rounding you want is to add 0.5 and then take the floor. For example, adding 0.5 to 2.5 gives 3, with floor 3.
Related videos on Youtube
Comments
-
Delgan almost 2 years
I am facing a strange behavior of the
round()
function:for i in range(1, 15, 2): n = i / 2 print(n, "=>", round(n))
This code prints:
0.5 => 0 1.5 => 2 2.5 => 2 3.5 => 4 4.5 => 4 5.5 => 6 6.5 => 6
I expected the floating values to be always rounded up, but instead, it is rounded to the nearest even number.
Why such behavior, and what is the best way to get the correct result?
I tried to use the
fractions
but the result is the same.-
yurib over 8 yearscan't explain the behaviour of
round()
but you could usemath.ceil()
if you always want to round up -
Delgan over 8 years@yurib I would like
1.3
to be rounded down to1
, so I can not useceil()
. -
yurib over 8 yearsPossible duplicate of Limiting floats to two decimal points
-
StoryTeller - Unslander Monica over 8 yearsMany days have passed since I studied error analysis. However If I recall correctly, the rounding of
5*10**-k
depends on the digit preceding it. By rounding up for uneven digits and down for even digits, you get a positive error half the time and an even error half the time (in theory). When you perform many additions, those errors can cancel each-other
-
-
Robert E over 8 yearsIEEE 754 rounding half to even is also described at en.wikipedia.org/wiki/Rounding#Round_half_to_even
-
Mark Dickinson about 6 yearsThere are some subtleties that aren't addressed by this solution. E.g., what result does this give if
num = -2.4
? What aboutnum = 0.49999999999999994
?num = 5000000000000001.0
? On a typical machine using IEEE 754 format and semantics, this solution gives the wrong answer for all three of these cases. -
Matthew D. Scholefield about 6 years@Mark Dickinson I've updated the post to mention this. Thanks
-
dhobbs about 5 yearsIn your example, is there a benefit to modifying the local context as opposed to just using the
rounding
argument as in:n.to_integral_value(rounding=ROUND_HALF_UP)
? -
Martijn Pieters about 5 years@dhobbs: setting the context once is clearer in intent, but from a technical point of view there is no difference.
-
ShadowRanger over 4 yearsNow
x.499999999
will be subject to half-even rounding, and will (half the time, assuming floating point precision issues don't force it one way or the other) get rounded up. That's worse than the initial scenario, as you're now rounding to the more distant number. -
lalala over 3 yearsTime and time I am shocked there is not internal function like this. I mean, it is not like people nowadays use a lot of numpy and math in python to implement numerical algorithms....
-
kol about 3 yearsSure, it's the correct way if you are processing measured data, running simulations etc. But it's incorrect, for example, if you would like to calculate grades in school. In Hungary, we have a 5-grade grading system, and an average of 4.5 is rounded up to 5. I used this as an example when teaching my son Python, and I was stunned when round(4.5) gave 5. I had a hard time explaining to him why Python rounding works differently from what he learned in school about rounding...
-
Rocket Nikita almost 3 yearsDoes not add any value to other answers.
-
TeaCoast almost 3 years@RocketNikita Epic.
-
drws over 2 yearsIt does round 'up or down', but unfortunately doesn't work with negative numbers.
-
Uber Kluger over 2 yearsStrictly speaking, both 0.49999999999999994 and 5000000000000001.0 are problematic due to precision. In both cases, adding 0.5 causes necessary precision bits to "fall off" the right hand side of the IEEE 754 double (64bit) mantissa (52 fraction bits + implicit 1.0). The first case basically doubles the value, pushing the (set) LSB out, while the second case is so large that the 0.5 is smaller than the existing LSB value. In fact, for 2^52 <= num < 2^53, it will round integers to even (prob. due to math chip rounding 80bit internal back to 64bit output). num >= 2^53 adding 0.5 does nothing.
-
Uber Kluger over 2 yearsAnyone wondering about tmp.__round__, see Python built-in function source code - round() or Python 3 Decimal rounding half down with ROUND_HALF_UP context.
-
akushyn over 2 yearsGreat function! Never thought I'll face such an issue with rounding 133.125 with 2 digits to 133.13 nor to 133.12 . Thanks, Man!
-
Raihan Shafique almost 2 yearsthanks, super useful - directly used the 2nd code in the pandas df easily with apply df['round_halfup'] = df['original_column'].apply(lambda value: int(value + 0.5))