[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Float.round - should it be round-to-even

OliverMarchand

4/12/2006 9:15:00 AM

I am suggesting to change the Float.round function to use the
well-known round-to-even method in case of a tie, i.e. x.5, where the
distance to ceil and floor is equal.

round currently uses a round-away-from-zero strategy. This strategy
will introduce a bias when applied to many numbers, i.e. the average of
the rounded numbers will be higher than that of original numbers. This
effect does make a considerable difference, despite the singular nature
of the x.5 values. Consider e.g. the computation of the median of an
array with floats.

Here is a quick hack ruby implementation which adds round to even to
the Float class:

class Float

def signum
if self>=0.0; 1.0
else; -1.0
end
end

def roundte
s = self.signum
f = self*s

rp = f-f.floor
rm = f.ceil-f
if rp>rm
return (s*f.ceil).to_i
elsif rp<rm
return (s*f.floor).to_i
else
if (f.ceil%2)==0; return (s*f.ceil).to_i
else; return (s*f.floor).to_i
end
end
end

end

Round is implemented in float.c. Anyone interested in me implementing
round-to-even there?

tschuess,
Oliver

3 Answers

Stefano Taschini

4/12/2006 10:46:00 AM

0

> Consider e.g. the computation of the median of an array with floats.

If your input data are floats and your result is also a float, may ask
you why you want to round them?

If you are actually thinking of rounding errors, i.e., the fact that
(1/2.0) + (1/3.0) != 5/6.0, well, they are independent of Float#round.

By the way, the median [1] is hardly biased by rounding errors. It's
the mean that is sensitive to that.

Ciao,
Stefano

[1] http://mathworld.wolfram.com/StatisticalM...

OliverMarchand

4/12/2006 12:06:00 PM

0

Stefano, thanks for your input!

> If your input data are floats and your result is also a float, may ask
> you why you want to round them?

I used a (as I saw now) non-standard definition of the median, in ruby
say:

median =
array_with_floats.sort[(array_with_floats.length/2).to_f.round]

where the median is ensured to be an element of the list of values.

With the standard definition from [1], there is no rounding effect -
yes.

I have no formal analysis of this at hand or cannot quickly derive one,
but I am pretty sure that using round-to-even for my median is
statistically "better" in a useful sense then using
round-away-from-zero.

cheers,
Oliver

David Sletten

4/14/2006 11:43:00 AM

0

OliverMarchand wrote:

> I am suggesting to change the Float.round function to use the
> well-known round-to-even method in case of a tie, i.e. x.5, where the
> distance to ceil and floor is equal.
>
> round currently uses a round-away-from-zero strategy. This strategy
> will introduce a bias when applied to many numbers, i.e. the average of
> the rounded numbers will be higher than that of original numbers. This
> effect does make a considerable difference, despite the singular nature
> of the x.5 values. Consider e.g. the computation of the median of an
> array with floats.
>
> Here is a quick hack ruby implementation which adds round to even to
> the Float class:
>
> class Float
>
> def signum
> if self>=0.0; 1.0
> else; -1.0
> end
> end
>

Signum of 0 is 0, not 1.
Here's the classic definition:
def signum(x)
if x.zero? then x
else x.abs/x
end
end

> def roundte
> s = self.signum
> f = self*s
>
> rp = f-f.floor
> rm = f.ceil-f
> if rp>rm
> return (s*f.ceil).to_i
> elsif rp<rm
> return (s*f.floor).to_i
> else
> if (f.ceil%2)==0; return (s*f.ceil).to_i
> else; return (s*f.floor).to_i
> end
> end
> end
>
> end
>

You seem to be doing a lot of excess work here. How about:
class Float
def signum
if self.zero? then self
else self.abs/self
end
end

def roundte
s = self.signum
f = self.abs

floor = f.floor
ceiling = f.ceil

rp = f-floor
rm = ceiling-f
if rp>rm
return (s*ceiling).to_i
elsif rp<rm
return (s*floor).to_i
else
if (ceiling%2)==0; return (s*ceiling).to_i
else; return (s*floor).to_i
end
end
end
end


But isn't this simpler?
class Float
def round_twd_even
if self.zero? then self.to_i
elsif self < 0 then -((-self).round_twd_even)
else
floor = self.floor
if self-floor == 0.5 and floor%2 == 0 then
floor
else
self.round
end
end
end
end

Or if you really don't like the recursion:
class Float
def round_twd_even
if self.zero? then self.to_i
elsif self < 0
ceiling = self.ceil
if ceiling-self == 0.5 and ceiling%2 == 0 then
ceiling
else
self.round
end
else
floor = self.floor
if self-floor == 0.5 and floor%2 == 0 then
floor
else
self.round
end
end
end
end

Or just for fun:
class Float
def round_twd_even
if self.zero? then self.to_i
else
if self < 0
int = self.ceil
else
int = self.floor
end

if (self-int).abs == 0.5 and int%2 == 0 then
int
else
self.round
end
end
end
end

It's safe to compare for equality with 0.5 since this is a negative
integral power of 2 and consequently can be represented exactly as a float.

Aloha,
David Sletten