Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss
Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Rounding a number to nearest even

16 views
Skip to first unread message

bdsatish

unread,
Apr 11, 2008, 6:14:15 AM4/11/08
to
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 ?

colas....@gmail.com

unread,
Apr 11, 2008, 6:27:44 AM4/11/08
to

When you say "round to the nearest even", you mean new_round(3) <> 3?

Is so, you can try:

In [37]: def new_round(x):
....: return round(x/2.)*2
....:

In [38]: new_round(1.5)
Out[38]: 2.0

In [39]: new_round(2.5)
Out[39]: 2.0

In [40]: new_round(3)
Out[40]: 4.0

bdsatish

unread,
Apr 11, 2008, 7:07:51 AM4/11/08
to
On Apr 11, 3:27 pm, colas.fran...@gmail.com wrote:
> On 11 avr, 12:14, bdsatish <bdsat...@gmail.com> 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 ?
>
> When you say "round to the nearest even", you mean new_round(3) <> 3?

No. not at all. The clause "nearest even" comes into picture only when
a number is of form "x.5" or else it's same as builtin round( ).
new_round(3.0) must be 3.0 itself. Here is the mathematical definition
of what I want:

If 'n' is an integer,

new_round(n+0.5) = n if n/2 is integer
new_round(n+0.5) = (n+1) if (n+1)/2 is integer

In all other cases, new_round() behave similarly as round( ). Here are
the results I expect:

new_round(3.2) = 3
new_round(3.6) = 4
new_round(3.5) = 4
new_round(2.5) = 2
new_round(-0.5) = 0.0
new_round(-1.5) = -2.0
new_round(-1.3) = -1.0
new_round(-1.8) = -2
new_round(-2.5) = -2.0

The built-in function doesnt meet my needs for round(-2.5) or
round(2.5)

cokof...@gmail.com

unread,
Apr 11, 2008, 7:19:29 AM4/11/08
to
couldn't you just do.

#untested
new_round(n):
answer = round(n)
# is answer now odd
if answer % 2:
return answer - 1
else:
return answer

cokof...@gmail.com

unread,
Apr 11, 2008, 7:24:35 AM4/11/08
to

Whoops, this also affects odd numbers...

Will try and find a GOOD solution later...

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

bdsatish

unread,
Apr 11, 2008, 7:27:06 AM4/11/08
to

It fails for negative numbers: For -2.5 it gives -4.0 as answer
whereas I expect -2.0

Scott David Daniels

unread,
Apr 11, 2008, 7:37:03 AM4/11/08
to

def rounded(v):
rounded = round(v)
if divmod(v, 1)[1] == .5 and divmod(rounded, 2)[1] == 1:
if v > 0:
return rounded - 1
return rounded + 1
return rounded

last = None
for n in range(-29, 28):
x = n * .25
r = xr(x)
if r != last:
last = r
print
print '%s->%s' % (x, xr(x)),

-7.25->-7.0 -7.0->-7.0 -6.75->-7.0
-6.5->-6.0 -6.25->-6.0 -6.0->-6.0 -5.75->-6.0 -5.5->-6.0
-5.25->-5.0 -5.0->-5.0 -4.75->-5.0
-4.5->-4.0 -4.25->-4.0 -4.0->-4.0 -3.75->-4.0 -3.5->-4.0
-3.25->-3.0 -3.0->-3.0 -2.75->-3.0
-2.5->-2.0 -2.25->-2.0 -2.0->-2.0 -1.75->-2.0 -1.5->-2.0
-1.25->-1.0 -1.0->-1.0 -0.75->-1.0
-0.5->0.0 -0.25->-0.0 0.0->0.0 0.25->0.0 0.5->0.0
0.75->1.0 1.0->1.0 1.25->1.0
1.5->2.0 1.75->2.0 2.0->2.0 2.25->2.0 2.5->2.0
2.75->3.0 3.0->3.0 3.25->3.0
3.5->4.0 3.75->4.0 4.0->4.0 4.25->4.0 4.5->4.0
4.75->5.0 5.0->5.0 5.25->5.0
5.5->6.0 5.75->6.0 6.0->6.0 6.25->6.0 6.5->6.0
6.75->7.0

bdsatish

unread,
Apr 11, 2008, 7:32:13 AM4/11/08
to

It also fails for negative numbers. For -2.5 as input, I get -4.0
whereas I expect -2.0

This is a lengthy solution I came-up with:

def round_even(x):
temp = round(abs(x))
if (abs(x) - 0.5)%2.0 == 0.0: temp=temp-1
return signum(x)*temp

def signum(x):
if x>0: return 1
if x<0: return -1
return 0

But i guess there are better ways. I need it 'cos I'm translating some
code from Mathematica to Python. And Math..ica's Round[ ] behaves this
way (as I requested)

bdsatish

unread,
Apr 11, 2008, 7:42:22 AM4/11/08
to
On Apr 11, 4:37 pm, Scott David Daniels <Scott.Dani...@Acm.Org> wrote:
> bdsatish wrote:
> > The built-in function round( ) will always "round up", that is 1.5 is
> def rounded(v):
> rounded = round(v)
> if divmod(v, 1)[1] == .5 and divmod(rounded, 2)[1] == 1:
> if v > 0:
> return rounded - 1
> return rounded + 1
> return rounded
>
> last = None
> for n in range(-29, 28):
> x = n * .25
> r = xr(x)
> if r != last:
> last = r
> print
> print '%s->%s' % (x, xr(x)),
>

Hi Scott,
This is what I was looking for.. I forgot about divmod( ) thanks for
reminding.

Gerard Flanagan

unread,
Apr 11, 2008, 8:05:37 AM4/11/08
to

------------------------------------------------
def myround(x):
n = int(x)
if abs(x - n) == 0.5:
if n % 2:
#it's odd
return n + 1 - 2 * int(n<0)
else:
return n
else:
return round(x)

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.8) == -2
assert myround(-2.5) == -2.0
------------------------------------------------

Gerard Flanagan

unread,
Apr 11, 2008, 8:14:13 AM4/11/08
to
On Apr 11, 2:05 pm, Gerard Flanagan <grflana...@gmail.com> wrote:
> On Apr 11, 12:14 pm, bdsatish <bdsat...@gmail.com> 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 myround(x):
> n = int(x)
> if abs(x - n) == 0.5:
> if n % 2:
> #it's odd
> return n + 1 - 2 * int(n<0)
> else:
> return n
> else:
> return round(x)
>
> ------------------------------------------------

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

bdsatish

unread,
Apr 11, 2008, 8:33:53 AM4/11/08
to
HI Gerard,

I think you've taken it to the best possible implementation. Thanks !

colas....@gmail.com

unread,
Apr 11, 2008, 8:43:42 AM4/11/08
to
On 11 avr, 14:14, Gerard Flanagan <grflana...@gmail.com> wrote:
> On Apr 11, 2:05 pm, Gerard Flanagan <grflana...@gmail.com> wrote:
>
> > On Apr 11, 12:14 pm, bdsatish <bdsat...@gmail.com> 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.8) == -2

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

bdsatish

unread,
Apr 11, 2008, 8:45:58 AM4/11/08
to
On Apr 11, 5:33 pm, bdsatish <bdsat...@gmail.com> wrote:
> HI Gerard,
>
> I think you've taken it to the best possible implementation. Thanks !
> On Apr 11, 5:14 pm, Gerard Flanagan <grflana...@gmail.com> 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.8) == -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

unread,
Apr 11, 2008, 8:58:46 AM4/11/08
to

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

unread,
Apr 11, 2008, 9:49:23 AM4/11/08
to

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

unread,
Apr 11, 2008, 10:13:28 AM4/11/08
to

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

unread,
Apr 11, 2008, 10:29:31 AM4/11/08
to
On Apr 11, 11:13 am, Ivan Illarionov <ivan.illario...@gmail.com>
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. :-P

Graham Breed

unread,
Apr 11, 2008, 10:50:33 AM4/11/08
to
On Apr 11, 6:14 pm, bdsatish <bdsat...@gmail.com> 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
instead of floats.

> 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

unread,
Apr 11, 2008, 11:19:50 AM4/11/08
to
cokof...@gmail.com 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

unread,
Apr 11, 2008, 1:55:28 PM4/11/08
to pytho...@python.org
cokof...@gmail.com 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
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco

Mark Dickinson

unread,
Apr 11, 2008, 2:05:00 PM4/11/08
to
On Apr 11, 10:29 am, hdante <hda...@gmail.com> wrote:
> Strangely, a "faster" version is:
>
> def fast_round(x):
> if x % 1 != 0.5: return round(x)
> return 2.0*round(x/2.0)

You should be a little bit careful with the test
x%1 == 0.5 if x might be negative:

>>> x = -0.5 + 2**-54
>>> x # not an exact half...
-0.49999999999999994
>>> x % 1 # ... and yet x%1 == 0.5
0.5

But for x positive, it should be safe. And for this
particular application, it turns out that it doesn't
matter: it gives the right result for this input anyway.

Mark

Lie

unread,
Apr 11, 2008, 2:33:56 PM4/11/08
to
On Apr 11, 10:19 pm, Mikael Olofsson <mik...@isy.liu.se> wrote:
> cokofree...@gmail.com 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.

That old-school rounding method you're taught is based on a wrong
assumption of the nature of number. In the past, rounding algorithm is
based on this:

Original => (RoundUp(u|d|n), RoundNearestEven(u|d|n)
...
1.0 => 1(n), 1(n)
1.1 => 1(d), 1(d)
1.2 => 1(d), 1(d)
1.3 => 1(d), 1(d)
1.4 => 1(d), 1(d)
1.5 => 2(u), 2(u)
1.6 => 2(u), 2(u)
1.7 => 2(u), 2(u)
1.8 => 2(u), 2(u)
1.9 => 2(u), 2(u)
2.0 => 2(n), 2(n)
2.1 => 2(d), 2(d)
2.2 => 2(d), 2(d)
2.3 => 2(d), 2(d)
2.4 => 2(d), 2(d)
2.5 => 3(u), 2(d)
2.6 => 3(u), 3(u)
2.7 => 3(u), 3(u)
2.8 => 3(u), 3(u)
2.9 => 3(u), 3(u)
...

In this used-to-be-thought-correct table, Round Ups algorithm have 2
Unrounded, 8 Round Down, and 10 Round Ups which seems incorrect while
Round Even have 2 Unrounded, 9 Round Down, and 9 Round Up which seems
correct. The misunderstanding comes from a view that thinks that there
is such thing as Not Rounded while in fact the only number that is Not
Rounded is 1 and 2 while 1.0 and 2.0 must still be rounded, in
practice we can just say that all number must be rounded somewhere.

Original => (RoundUp(u|d), RoundNearestEven(u|d)
...
1.0 => 1(d), 1(d)
1.1 => 1(d), 1(d)
1.2 => 1(d), 1(d)
1.3 => 1(d), 1(d)
1.4 => 1(d), 1(d)
1.5 => 2(u), 2(u)
1.6 => 2(u), 2(u)
1.7 => 2(u), 2(u)
1.8 => 2(u), 2(u)
1.9 => 2(u), 2(u)
2.0 => 2(d), 2(d)
2.1 => 2(d), 2(d)
2.2 => 2(d), 2(d)
2.3 => 2(d), 2(d)
2.4 => 2(d), 2(d)
2.5 => 3(u), 2(d)
2.6 => 3(u), 3(u)
2.7 => 3(u), 3(u)
2.8 => 3(u), 3(u)
2.9 => 3(u), 3(u)
...

In this table, we consider that a number is rounded down when the
number is equal to truncated value (the number without fractional
part), while round up is equal to truncated value + 1 or truncated
value -1 if value is negative (Actually this is not round-half-up
algorithm, it's a round-half-away-from-zero algorithm, but lets just
consider that to be the same for now). In this revised table, you get
10 round ups and 10 round down (i.e. Average Rounding Error == 0),
while by rounding to nearest even you get 9 round up and 11 round down
(i.e. Average Rounding Error != 0).

> 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.

Another mistake, in an unquantized value the probability of getting
exactly 0.5 (or any other number specified) is not 0 but an
infinitesimal (i.e. lim(x) where x -> 0 (BUT NOT 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.

No the reason is not that, it's because 1) it is much easier to input
integral numbers to computer (calculator or cashier machine), to
input .5 to computer you have to press two more buttons or at least
one shortcut button and 2) if you have two lists, one with a bunch of .
5's and the other just a few of them you would know that it's easier
to sum the latter list with or without calculator.


> 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.

A false assertion from a false understanding. The use of Round Up
algorithm is a refinement of the false understanding we used to
practice in the past.

Mark Dickinson

unread,
Apr 11, 2008, 3:27:43 PM4/11/08
to
On Apr 11, 2:33 pm, Lie <Lie.1...@gmail.com> wrote:
> In this table, we consider that a number is rounded down when the
> number is equal to truncated value (the number without fractional
> part), while round up is equal to truncated value + 1 or truncated
> value -1 if value is negative (Actually this is not round-half-up
> algorithm, it's a round-half-away-from-zero algorithm, but lets just
> consider that to be the same for now). In this revised table, you get
> 10 round ups and 10 round down (i.e. Average Rounding Error == 0),
> while by rounding to nearest even you get 9 round up and 11 round down
> (i.e. Average Rounding Error != 0).

No: if you interpret average to mean 'mean' (add up the (signed)
rounding errors from the 20 cases you list and divide by 20) you'll
find that the average rounding error is indeed 0 for round-half-to-
even,
and 0.05 for round-half-away-from-0, just as Mikael said.

> Another mistake, in an unquantized value the probability of getting
> exactly 0.5 (or any other number specified) is not 0 but an
> infinitesimal (i.e. lim(x) where x -> 0 (BUT NOT ZERO))

I'm not sure you'll get many takers for this point of view. If X is
a random variable uniformly distributed on the interval [0, 1) then
the probability that X == 0.5 is indeed exactly 0, using conventional
definitions. (And if you're not using conventional definitions, you
should say so....)

Mark

Gabriel Genellina

unread,
Apr 11, 2008, 4:29:03 PM4/11/08
to

That's not correct. If the numbers to be rounded come from a
measurement, the left column is not just a number but the representant
of an interval (as Mikael said, the're quantized). 2.3 means that the
measurement was closer to 2.3 than to 2.2 or 2.4 - that is, [2.25,
2.35) (it doesn't really matter which side is open or closed). It is
this "interval" behavior that forces the "round-to-even-on-halves"
rule.
So, the numbers 1.6-2.4 on the left column cover the interval [1.55,
2.45) and there is no doubt that they should be rounded to 2.0 because
all of them are closer to 2.0 than to any other integer. Similarly
[2.55, 3.45) are all rounded to 3.
But what to do with [2.45, 2.55), the interval represented by 2.5? We
can assume a uniform distribution here even if the whole distribution
is not (because we're talking of the smallest measurable range). So
half of the time the "true value" would have been < 2.5, and we should
round to 2. And half of the time it's > 2.5 and we should round to 3.
Rounding always to 3 introduces a certain bias in the process.
Rounding randomly (tossing a coin, by example) would be fair, but
people usually prefer more deterministic approaches. If the number of
intervals is not so small, the "round even" rule provides a way to
choose from that two possibilities with equal probability.
So when we round 2.5 we are actually rounding an interval which could
be equally be rounded to 2 or to 3, and the same for 3.5, 4.5 etc. If
the number of such intervals is big, choosing the even number helps to
make as many rounds up as rounds down.
If the number of such intervals is small, *any* apriori rule will
introduce a bias.

> > 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.
>
> Another mistake, in an unquantized value the probability of getting
> exactly 0.5 (or any other number specified) is not 0 but an
> infinitesimal (i.e. lim(x) where x -> 0 (BUT NOT ZERO))

That limit IS zero. And the probability of getting exactly a certain
real number, or any finite set of real numbers, is zero too (assuming
the usual definition of probability over infinite sets). But we're not
actually talking about real numbers here.

--
Gabriel Genellina

hdante

unread,
Apr 11, 2008, 4:44:15 PM4/11/08
to

But then, the "Round up" table gives inconsistent results if, by the
same argument, we consider 2.0 -> 2 rounding up. (you get 12 round ups
and 8 round downs just by "rethinking" the argument). So, "rounding
up" is, at the same time, better and worse than rounding to nearest
even.

> > 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.
>
> A false assertion from a false understanding. The use of Round Up
> algorithm is a refinement of the false understanding we used to
> practice in the past.

The assertion is false, because there's no problem in rounding up,
truncating, etc. Just consider the amount of code snippets in this
thread. A simpler solution may solve a lot of problems.

Arnaud Delobelle

unread,
Apr 11, 2008, 4:48:44 PM4/11/08
to
On Apr 11, 8:27 pm, Mark Dickinson <dicki...@gmail.com> wrote:
> On Apr 11, 2:33 pm, Lie <Lie.1...@gmail.com> wrote:
[...]

> > Another mistake, in an unquantized value the probability of getting
> > exactly 0.5 (or any other number specified) is not 0 but an
> > infinitesimal (i.e. lim(x) where x -> 0 (BUT NOT ZERO))
>
> I'm not sure you'll get many takers for this point of view.  If X is
> a random variable uniformly distributed on the interval [0, 1) then
> the probability that X == 0.5 is indeed exactly 0, using conventional
> definitions.  (And if you're not using conventional definitions, you
> should say so....)

And I would like to know what unconventional - but mathematically
meaningful - definitions lead to

lim x != 0
x -> 0

--
Arnaud

Lie

unread,
Apr 13, 2008, 4:18:24 AM4/13/08
to
On Apr 12, 3:44 am, hdante <hda...@gmail.com> wrote:
(snip)

> > In this table, we consider that a number is rounded down when the
>
> But then, the "Round up" table gives inconsistent results if, by the
> same argument, we consider 2.0 -> 2 rounding up. (you get 12 round ups
> and 8 round downs just by "rethinking" the argument). So, "rounding
> up" is, at the same time, better and worse than rounding to nearest
> even.
>

It's not round up, why? In the usual sense -- when not comparing
against round-half-even -- the number range we're talking is from x to
lim((x+1)-y)[y -> 0 from the positive side], e.g. 1 to nearly 2 (thus
the integer 2 itself is not included in the number range we're talking
since it belongs to the next set), then we choose a uniformly
distributed samples, i.e. 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8,
and 1.9. From these samples, we chose which are rounded down and which
are rounded up, it happens that 1.0 is rounded down, while 1.5 is
rounded up.

IF for the sake of argument, you choose the number range to be lim(x +
y)[y -> 0 from the positive side] to x + 1, e.g. barely above 1 to 2,
then the number sample you use is 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7,
1.8, 1.9, and 2.0, in this second number range, there are 5 round
downs (1.1, 1.2, 1.3, 1.4, 1.5) and 5 round ups (1.6, 1.7, 1.8, 1.9,
2.0), but how logical is it to choose this number range (barely above
1 to 2) against choosing the more natural range (1 to nearly 2): in
the correctly chosen number range (1 to nearly 2) all numbers in the
form of 1.x (where x is any positive number and 0) is contained within
it and there is nothing else in it, but in the second number range
(barely above 1 to 2) the number 1.0 is not included while the number
2.0 is contained in it, clearly not a clean separation of numbers in
the form of y.x where y is pre-determined and x is variable from other
possible values of y.

In short, choosing that x.0 is rounded down and x.5 is rounded up is
arbitrary but not without a reason.

Steve Holden

unread,
Apr 13, 2008, 8:20:05 AM4/13/08
to pytho...@python.org
Lie wrote:
> On Apr 12, 3:44 am, hdante <hda...@gmail.com> wrote:
[snip]

>
> In short, choosing that x.0 is rounded down and x.5 is rounded up is
> arbitrary but not without a reason.

Don't "arbitrary" and "not without a reason" directly contradict one
another?

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
Holden Web LLC http://www.holdenweb.com/

Mark Dickinson

unread,
Apr 13, 2008, 10:28:22 AM4/13/08
to
On Apr 13, 4:18 am, Lie <Lie.1...@gmail.com> wrote:
[...]

> it and there is nothing else in it, but in the second number range
> (barely above 1 to 2) the number 1.0 is not included while the number
> 2.0 is contained in it, clearly not a clean separation of numbers in
> the form of y.x where y is pre-determined and x is variable from other
> possible values of y.

Have you considered the fact that the real numbers of
the form 1.xxxxx... are those in the range [1.0, 2.0],
including *both* endpoints? That is, 2.0 = 1.999999...
Similarly, the midpoint of this range can be written
both in the form 1.500000... and 1.499999...

This is relevant if you think of rounding as an operation
on *decimal representations* of real numbers rather than
as an operation on real numbers themselves. I'm not sure
which point of view you're taking here.

Either way, your arguments don't change the fact that the
average rounding error is strictly positive for positive
quantized results, under round-half-away-from-zero.

Mark

andre...@gmail.com

unread,
Apr 14, 2008, 11:01:28 AM4/14/08
to
On Apr 11, 4:14 am, bdsatish <bdsat...@gmail.com> wrote:

> 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 ?

Side note: A specialized use for this is in the US Field Artillery,
where it's called "artillery expression." You don't round, but
"express" a fraction to the nearest even whole number. 2.5 is 2.0,
3.5 is 4.0, etc. Or, if you need to express to the tens, 15 is 20, 25
is 20, and so on.

Thomas Dybdahl Ahle

unread,
Apr 14, 2008, 2:02:48 PM4/14/08
to bdsatish, pytho...@python.org
On Fri, 2008-04-11 at 03:14 -0700, bdsatish 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 ?

This seams to work fine:
evenRound = lambda f: round(f/2.)*2

>>> [(f*.5, evenRound(f*.5)) for f in xrange(0,20)]
[(0.0, 0.0),(0.5, 0.0),
(1.0, 2.0), (1.5, 2.0), (2.0, 2.0), (2.5, 2.0),
(3.0, 4.0), (3.5, 4.0), (4.0, 4.0), (4.5, 4.0),
(5.0, 6.0), (5.5, 6.0), (6.0, 6.0), (6.5, 6.0),
(7.0, 8.0), (7.5, 8.0), (8.0, 8.0), (8.5, 8.0),
(9.0, 10.0), (9.5, 10.0)]

--
Best Regards,
Med Venlig Hilsen,
Thomas

Sjoerd Mullender

unread,
Apr 15, 2008, 5:22:16 AM4/15/08
to Thomas Dybdahl Ahle, pytho...@python.org, bdsatish

No, this does not work:
>>> [(f*.25, evenRound(f*.25)) for f in xrange(0,20)]
[(0.0, 0.0), (0.25, 0.0), (0.5, 0.0), (0.75, 0.0), (1.0, 2.0), (1.25,
2.0), (1.5, 2.0), (1.75, 2.0), (2.0, 2.0), (2.25, 2.0), (2.5, 2.0),
(2.75, 2.0), (3.0, 4.0), (3.25, 4.0), (3.5, 4.0), (3.75, 4.0), (4.0,
4.0), (4.25, 4.0), (4.5, 4.0), (4.75, 4.0)]

x.75 should be rounded up.

--
Sjoerd Mullender

signature.asc

Chris

unread,
Apr 15, 2008, 5:31:36 AM4/15/08
to
>  signature.asc
> 1KDownload

even is closer to even.75 than even+1.25. Why should it be rounded
up ?

colas....@gmail.com

unread,
Apr 15, 2008, 5:37:28 AM4/15/08
to
On 14 avr, 20:02, Thomas Dybdahl Ahle <lob...@gmail.com> wrote:
> On Fri, 2008-04-11 at 03:14 -0700, bdsatish 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 ?
>
> This seams to work fine:
> evenRound = lambda f: round(f/2.)*2

That was the solution I proposed first but it is inadequate since the
op does not want 3 to be rounded to 4.
If you're interested in this discussion, I kindly suggest you read the
whole thread: many interesting proposals and discussions have ensued.

Duncan Booth

unread,
Apr 15, 2008, 5:47:27 AM4/15/08
to
Chris <cwi...@gmail.com> wrote:

> even is closer to even.75 than even+1.25. Why should it be rounded
> up ?

Because the OP wants to round values to the nearest integer. Only values of
the form 'x.5' which have two nearest values use 'nearest even' to
disambiguate the result.

See http://en.wikipedia.org/wiki/Rounding#Round-to-even_method

That's the way I was taught to round numbers when at primary school.

Chris

unread,
Apr 15, 2008, 6:33:03 AM4/15/08
to
On Apr 15, 11:47 am, Duncan Booth <duncan.bo...@invalid.invalid>
wrote:

> Chris <cwi...@gmail.com> wrote:
> > even is closer to even.75 than even+1.25.  Why should it be rounded
> > up ?
>
> Because the OP wants to round values to the nearest integer. Only values of
> the form 'x.5' which have two nearest values use 'nearest even' to
> disambiguate the result.
>
> Seehttp://en.wikipedia.org/wiki/Rounding#Round-to-even_method

>
> That's the way I was taught to round numbers when at primary school.

My bad, didn't see he only wanted for halves and handle others as
normal.

Chris

unread,
Apr 15, 2008, 6:47:11 AM4/15/08
to

My contribution then:

def my_round(x):
if x < 0:
NEG = 1
x = -x
else:
NEG = 0
if not x%.5 and not int(x)%2:
if NEG: return -round(x-.1)
return round(x-.1)
elif not x%.5 and int(x)%2:
if NEG: return -round(x+.1)
return round(x+.1)
elif NEG:
return round(-x)
else:
return round(x)

[(f*.25, my_round(f*.25)) for f in xrange(-20,20)]

[(-5.0, -5.0), (-4.75, -5.0), (-4.5, -4.0), (-4.25, -4.0), (-4.0,
-4.0), (-3.75, -4.0), (-3.5, -4.0), (-3.25, -3.0), (-3.0, -3.0),
(-2.75, -3.0), (-2.5, -2.0), (-2.25, -2.0), (-2.0, -2.0), (-1.75,
-2.0), (-1.5, -2.0), (-1.25, -1.0), (-1.0, -1.0), (-0.75, -1.0),
(-0.5, 0.0), (-0.25, 0.0), (0.0, 0.0), (0.25, 0.0), (0.5, 0.0), (0.75,
1.0), (1.0, 1.0), (1.25, 1.0), (1.5, 2.0), (1.75, 2.0), (2.0, 2.0),
(2.25, 2.0), (2.5, 2.0), (2.75, 3.0), (3.0, 3.0), (3.25, 3.0), (3.5,
4.0), (3.75, 4.0), (4.0, 4.0), (4.25, 4.0), (4.5, 4.0), (4.75, 5.0)]

Arnaud Delobelle

unread,
Apr 15, 2008, 7:14:00 AM4/15/08
to
On 11 Apr, 21:29, Gabriel Genellina <gagsl-...@yahoo.com.ar> wrote:
> ... If the numbers to be rounded come from a

Great explanation!

--
Arnaud

Lie

unread,
Apr 17, 2008, 3:35:25 PM4/17/08
to
On Apr 13, 7:20 pm, Steve Holden <st...@holdenweb.com> wrote:
> Lie wrote:
> > On Apr 12, 3:44 am, hdante <hda...@gmail.com> wrote:
> [snip]
>
> > In short, choosing that x.0 is rounded down and x.5 is rounded up is
> > arbitrary but not without a reason.
>
> Don't "arbitrary" and "not without a reason" directly contradict one
> another?
>

The same as choosing between round-half-odd and round-half-even,


arbitrary but not without a reason.

Language-wise it is a contradiction. In a loose semantic meaning, it
means there is no strong reason for choosing one above the other
(round-up vs round down and round-half-even vs round-half-odd) but
there are slight advantages on one that would seems silly to be
mentioned as the main reason, so arbitrary but not without reason.

On Apr 13, 9:28 pm, Mark Dickinson <dicki...@gmail.com> wrote:
> On Apr 13, 4:18 am, Lie <Lie.1...@gmail.com> wrote:
> [...]
>

> > it and there is nothing else in it, but in the second number range
> > (barely above 1 to 2) the number 1.0 is not included while the number
> > 2.0 is contained in it, clearly not a clean separation of numbers in
> > the form of y.x where y is pre-determined and x is variable from other
> > possible values of y.
>

> Have you considered the fact that the real numbers of
> the form 1.xxxxx... are those in the range [1.0, 2.0],
> including *both* endpoints? That is, 2.0 = 1.999999...
> Similarly, the midpoint of this range can be written
> both in the form 1.500000... and 1.499999...

No, you can only include one of the endpoints, because if both
endpoints are included, the integers would be a included twice on the
various sets of numbers, e.g. in [1.0 - 2.0] and [2.0 - 3.0] the
number 2.0 is included in both ranges, you can't be a member of both
ranges because that means if you have a "fair" random number
generator, the possibility of integral number to appear would be twice
the possibility of non-integral numbers, well that's not a fair random
number generator isn't it?

> This is relevant if you think of rounding as an operation
> on *decimal representations* of real numbers rather than
> as an operation on real numbers themselves. I'm not sure
> which point of view you're taking here.

Actually, I'm on the side that think numbers should never be
rounded[1], except for a final representation to human viewer who
should be made aware that the numbers in the presentation should never
be used for further calculation, as they are only informative but not
normative. In this view, the exact rounding method is irrelevant as
numbers used for calculation are never rounded while at the lower
level the amount of change caused by rounding has become irrelevant
because of the extra precision used. In short, if the rounding
algorithm causes you a trouble in the particular field, increase the
precision.

[1] As computers do have limitation on storage of real number
representation, this statement means that real numbers should be kept
as precise as the hardware allows or to use decimal and allow extra
precision generously.

> Either way, your arguments don't change the fact that the
> average rounding error is strictly positive for positive
> quantized results, under round-half-away-from-zero.

If you include negative numbers, the ARE is zero on round-half-away-
from-zero, not in round-up though.

0 new messages