Velocity Reviews > Rounding a number to nearest even

# Rounding a number to nearest even

bdsatish
Guest
Posts: n/a

 04-11-2008
HI Gerard,

I think you've taken it to the best possible implementation. Thanks !
On Apr 11, 5:14 pm, Gerard Flanagan <(E-Mail Removed)> wrote:
> In fact you can avoid the call to the builtin round:
>
> ------------------------------------------------
> def myround(x):
> n = int(x)
> if abs(x - n) >= 0.5 and n % 2:
> return n + 1 - 2 * int(n<0)
> else:
> return n
>
> assert myround(3.2) == 3
> assert myround(3.6) == 4
> assert myround(3.5) == 4
> assert myround(2.5) == 2
> assert myround(-0.5) == 0.0
> assert myround(-1.5) == -2.0
> assert myround(-1.3) == -1.0
> assert myround(-1. == -2
> assert myround(-2.5) == -2.0
> ------------------------------------------------

colas.francis@gmail.com
Guest
Posts: n/a

 04-11-2008
On 11 avr, 14:14, Gerard Flanagan <(E-Mail Removed)> wrote:
> On Apr 11, 2:05 pm, Gerard Flanagan <(E-Mail Removed)> wrote:
>
> > On Apr 11, 12:14 pm, bdsatish <(E-Mail Removed)> wrote:

>
> > > The built-in function round( ) will always "round up", that is 1.5 is
> > > rounded to 2.0 and 2.5 is rounded to 3.0.

>
> > > If I want to round to the nearest even, that is

>
> > > my_round(1.5) = 2 # As expected
> > > my_round(2.5) = 2 # Not 3, which is an odd num

>
> > > I'm interested in rounding numbers of the form "x.5" depending upon
> > > whether x is odd or even. Any idea about how to implement it ?

> In fact you can avoid the call to the builtin round:

Alternatively, you can avoid the test using both divmod and round:

In [55]: def myround(x):
.....: d, m = divmod(x, 2)
.....: return 2*d + 1 + round(m-1)
.....:

In [58]: assert myround(3.2) == 3

In [59]: assert myround(3.6) == 4

In [60]: assert myround(3.5) == 4

In [61]: assert myround(2.5) == 2

In [62]: assert myround(-0.5) == 0.0

In [63]: assert myround(-1.5) == -2.0

In [64]: assert myround(-1.3) == -1.0

In [65]: assert myround(-1. == -2

In [66]: assert myround(-2.5) == -2.0

bdsatish
Guest
Posts: n/a

 04-11-2008
On Apr 11, 5:33 pm, bdsatish <(E-Mail Removed)> wrote:
> HI Gerard,
>
> I think you've taken it to the best possible implementation. Thanks !
> On Apr 11, 5:14 pm, Gerard Flanagan <(E-Mail Removed)> wrote:
>
> > In fact you can avoid the call to the builtin round:

>
> > ------------------------------------------------

>
> > assert myround(3.2) == 3
> > assert myround(3.6) == 4
> > assert myround(3.5) == 4
> > assert myround(2.5) == 2
> > assert myround(-0.5) == 0.0
> > assert myround(-1.5) == -2.0
> > assert myround(-1.3) == -1.0
> > assert myround(-1. == -2
> > assert myround(-2.5) == -2.0
> > ------------------------------------------------

OK, I was too early to praise Gerard. The following version:

def myround(x):
n = int(x)
if abs(x - n) >= 0.5 and n % 2:
return n + 1 - 2 * int(n<0)
else:
return n

of Gerard doesn't work for 0.6 (or 0.7, etc.) It gives the answer 0
but I would expect 1.0 ( because 0.6 doesn't end in 0.5 at all... so
usual rules of round( ) apply)

Ivan Illarionov
Guest
Posts: n/a

 04-11-2008
On Apr 11, 2:14 pm, bdsatish <(E-Mail Removed)> wrote:
> The built-in function round( ) will always "round up", that is 1.5 is
> rounded to 2.0 and 2.5 is rounded to 3.0.
>
> If I want to round to the nearest even, that is
>
> my_round(1.5) = 2 # As expected
> my_round(2.5) = 2 # Not 3, which is an odd num
>
> I'm interested in rounding numbers of the form "x.5" depending upon
> whether x is odd or even. Any idea about how to implement it ?

def even_round(x):
if x % 1 == .5 and not (int(x) % 2):
return float(int(x))
else:
return round(x)

nums = [ 3.2, 3.6, 3.5, 2.5, -.5, -1.5, -1.3, -1.8, -2.5 ]
for num in nums:
print num, '->', even_round(num)

3.2 -> 3.0
3.6 -> 4.0
3.5 -> 4.0
2.5 -> 2.0
-0.5 -> 0.0
-1.5 -> -2.0
-1.3 -> -1.0
-1.8 -> -2.0
-2.5 -> -2.0

hdante
Guest
Posts: n/a

 04-11-2008
On Apr 11, 9:45 am, bdsatish <(E-Mail Removed)> wrote:
> On Apr 11, 5:33 pm, bdsatish <(E-Mail Removed)> wrote:
>
>
>
> > HI Gerard,

>
> > I think you've taken it to the best possible implementation. Thanks !
> > On Apr 11, 5:14 pm, Gerard Flanagan <(E-Mail Removed)> wrote:

>
> > > In fact you can avoid the call to the builtin round:

>
> > > ------------------------------------------------

>
> > > assert myround(3.2) == 3
> > > assert myround(3.6) == 4
> > > assert myround(3.5) == 4
> > > assert myround(2.5) == 2
> > > assert myround(-0.5) == 0.0
> > > assert myround(-1.5) == -2.0
> > > assert myround(-1.3) == -1.0
> > > assert myround(-1. == -2
> > > assert myround(-2.5) == -2.0
> > > ------------------------------------------------

>
> OK, I was too early to praise Gerard. The following version:
>
> def myround(x):
> n = int(x)
> if abs(x - n) >= 0.5 and n % 2:
> return n + 1 - 2 * int(n<0)
> else:
> return n
>
> of Gerard doesn't work for 0.6 (or 0.7, etc.) It gives the answer 0
> but I would expect 1.0 ( because 0.6 doesn't end in 0.5 at all... so
> usual rules of round( ) apply)

Interestingly, you could solve this by using python 3.
round(x[, n])
Return the floating point value x rounded to n digits after the
decimal point. If n is omitted, it defaults to zero. Values are
rounded to the closest multiple of 10 to the power minus n; if two
multiples are equally close, rounding is done toward the even choice
(so, for example, both round(0.5) and round(-0.5) are 0, and
round(1.5) is 2). Delegates to x.__round__(n).

My turn:

def yaround(x):
i = int(x)
f = x - i
if f != 0.5 and f != -0.5: return round(x)
return 2.0*round(x/2.0)

a = (-10.0, -9.0, -8.0, -4.6, -4.5, -4.4, -4.0, -3.6, -3.5,
-3.4, -0.6, -0.5, -0.4, 0.0, 0.4, 0.5, 0.6, 0.9, 1.0,
1.4, 1.5, 1.6, 2.0, 2.4, 2.5, 2.6, 10.0, 100.0)

b = (-10.0, -9.0, -8.0, -5.0, -4.0, -4.0, -4.0, -4.0, -4.0,
-3.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 10.0, 100.0)

for i in range(len(a)):
assert yaround(a[i]) == b[i]

Ivan Illarionov
Guest
Posts: n/a

 04-11-2008
On Apr 11, 5:49 pm, hdante <(E-Mail Removed)> wrote:
> On Apr 11, 9:45 am, bdsatish <(E-Mail Removed)> wrote:
>
>
>
> > On Apr 11, 5:33 pm, bdsatish <(E-Mail Removed)> wrote:

>
> > > HI Gerard,

>
> > > I think you've taken it to the best possible implementation. Thanks !
> > > On Apr 11, 5:14 pm, Gerard Flanagan <(E-Mail Removed)> wrote:

>
> > > > In fact you can avoid the call to the builtin round:

>
> > > > ------------------------------------------------

>
> > > > assert myround(3.2) == 3
> > > > assert myround(3.6) == 4
> > > > assert myround(3.5) == 4
> > > > assert myround(2.5) == 2
> > > > assert myround(-0.5) == 0.0
> > > > assert myround(-1.5) == -2.0
> > > > assert myround(-1.3) == -1.0
> > > > assert myround(-1. == -2
> > > > assert myround(-2.5) == -2.0
> > > > ------------------------------------------------

>
> > OK, I was too early to praise Gerard. The following version:

>
> > def myround(x):
> > n = int(x)
> > if abs(x - n) >= 0.5 and n % 2:
> > return n + 1 - 2 * int(n<0)
> > else:
> > return n

>
> > of Gerard doesn't work for 0.6 (or 0.7, etc.) It gives the answer 0
> > but I would expect 1.0 ( because 0.6 doesn't end in 0.5 at all... so
> > usual rules of round( ) apply)

>
> Interestingly, you could solve this by using python 3.
> round(x[, n])
> Return the floating point value x rounded to n digits after the
> decimal point. If n is omitted, it defaults to zero. Values are
> rounded to the closest multiple of 10 to the power minus n; if two
> multiples are equally close, rounding is done toward the even choice
> (so, for example, both round(0.5) and round(-0.5) are 0, and
> round(1.5) is 2). Delegates to x.__round__(n).
>
> My turn:
>
> def yaround(x):
> i = int(x)
> f = x - i
> if f != 0.5 and f != -0.5: return round(x)
> return 2.0*round(x/2.0)
>
> a = (-10.0, -9.0, -8.0, -4.6, -4.5, -4.4, -4.0, -3.6, -3.5,
> -3.4, -0.6, -0.5, -0.4, 0.0, 0.4, 0.5, 0.6, 0.9, 1.0,
> 1.4, 1.5, 1.6, 2.0, 2.4, 2.5, 2.6, 10.0, 100.0)
>
> b = (-10.0, -9.0, -8.0, -5.0, -4.0, -4.0, -4.0, -4.0, -4.0,
> -3.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
> 1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 10.0, 100.0)
>
> for i in range(len(a)):
> assert yaround(a[i]) == b[i]

Shorter version:
def round3k(x):
return x % 1 != 0.5 and round(x) or round(x / 2.) * 2.

nums = [ 0, 2, 3.2, 3.6, 3.5, 2.5, -0.5, -1.5, -1.3, -1.8, -2.5, 0.6,
0.7 ]
rnums = [ 0, 2, 3.0, 4.0, 4.0, 2.0, -0.0, -2.0, -1.0, -2.0, -2.0, 1.0,
1.0 ]

for num, rnum in zip(nums, rnums):
assert even_round(num) == rnum, '%s != %s' % (even_round(num),
rnum)
print num, '->', even_round(num)

It makes sense to add `from __future__ import even_round` to Python
2.6.

hdante
Guest
Posts: n/a

 04-11-2008
On Apr 11, 11:13 am, Ivan Illarionov <(E-Mail Removed)>
wrote:
>
> Shorter version:
> def round3k(x):
> return x % 1 != 0.5 and round(x) or round(x / 2.) * 2.

Strangely, a "faster" version is:

def fast_round(x):
if x % 1 != 0.5: return round(x)
return 2.0*round(x/2.0)

>
> nums = [ 0, 2, 3.2, 3.6, 3.5, 2.5, -0.5, -1.5, -1.3, -1.8, -2.5, 0.6,
> 0.7 ]
> rnums = [ 0, 2, 3.0, 4.0, 4.0, 2.0, -0.0, -2.0, -1.0, -2.0, -2.0, 1.0,
> 1.0 ]

You shouldn't remove assertions in the smaller version.

>
> for num, rnum in zip(nums, rnums):
> assert even_round(num) == rnum, '%s != %s' % (even_round(num),
> rnum)
> print num, '->', even_round(num)
>
> It makes sense to add `from __future__ import even_round` to Python
> 2.6.

Graham Breed
Guest
Posts: n/a

 04-11-2008
On Apr 11, 6:14 pm, bdsatish <(E-Mail Removed)> wrote:
> The built-in function round( ) will always "round up", that is 1.5 is
> rounded to 2.0 and 2.5 is rounded to 3.0.
>
> If I want to round to the nearest even, that is
>
> my_round(1.5) = 2 # As expected
> my_round(2.5) = 2 # Not 3, which is an odd num

If you care about such details, you may be better off using decimals

> I'm interested in rounding numbers of the form "x.5" depending upon
> whether x is odd or even. Any idea about how to implement it ?

import decimal
decimal.Decimal("1.5").to_integral(
rounding=decimal.ROUND_HALF_EVEN)
decimal.Decimal("2.5").to_integral(
rounding=decimal.ROUND_HALF_EVEN)

ROUND_HALF_EVEN is the default, but maybe that can be changed, so
explicit is safest.

If you really insist,

import decimal
def my_round(f):
d = decimal.Decimal(str(f))
rounded = d.to_integral(rounding=decimal.ROUND_HALF_EVEN)
return int(rounded)

Graham

Mikael Olofsson
Guest
Posts: n/a

 04-11-2008
http://www.velocityreviews.com/forums/(E-Mail Removed) commented about rounding towards even numbers
from mid-way between integers as opposed to for instance always rounding
up in those cases:
> Strange request though, why do you need it that way, because 2.5 is
> CLOSER to 3 than to 2...

That's exactly how I was taught to do rounding in what-ever low-level
class it was. The idea is to avoid a bias, which assumes that the
original values are already quantized. Assume that we have values
quantized to one decimal only, and assume that all values of this
decimal are equally likely. Also assume that the integer part of our
numbers are equally likely to be even or odd. Then the average rounding
error when rounding to integers will be 0.05 if you always round up when
the decimal is 5. If you round towards an even number instead when the
decimal is 5, then you will round up half of those times, and round down
the other half, and the average rounding error will be 0. That's the
idea. Of course you could argue that it would be even more fair to make
the choice based on the tossing of a fair coin.

Note that if you do not have quantized values and assuming that the
fraction part is evenly distributed between 0 and 1, than this whole
argument is moot. The probability of getting exactly 0.5 is zero in that
case, just as the probability of getting any other specified number is zero.

That said, measurements are in practice always quantized, and rounding
towards an even number when mid-way between avoids an average error of
half the original precision.

As a side-note: The the smallest coin in Swedish currency SEK is 0.50,
but prices in stores are given with two decimals, i.e. with precision
0.01. But what if your goods add up to 12.34? The standard in Swedish
stores, after adding the prices of your goods, is to round the number to
the nearest whole or half SEK, which means that decimals 25 and 75 are
mid-way between. In those cases the rounding is usually done to the
nearest whole SEK, which is based on precicely the above reasoning. If
they did not do that, I could argue that they are stealing on average
0.005 SEK from me every time I go to the store. Well... I could live
with that, since 0.005 SEK is a ridiculously small amount, and even if I
make thousands of such transactions per year, it still sums up to a
neglectable amount.

Another side-note: My 12-year old son is now being taught to always
round up from mid-way between. Yet another example of the degradation of
maths in schools.

/MiO

Robert Kern
Guest
Posts: n/a

 04-11-2008
(E-Mail Removed) wrote:

> Strange request though, why do you need it that way, because 2.5 is
> CLOSER to 3 than to 2...

Uhhh, no it isn't. (3 - 2.5) == (2.5 - 2)

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
an underlying truth."
-- Umberto Eco