[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Re: Bug in % (Float)?

Peña, Botp

8/30/2007 2:15:00 AM

From: Charlie Lehardy [mailto:charlie.lehardy@gmail.com] :
# irb(main):001:0> 1 % 0.1
# => 0.1
#
# Shouldn't 1 % 0.1 be 0.0 and not 0.1?

never trust floats in division :)
ruby tries to be friendly at the expense of more surprises...

irb(main):343:0> system "qri numeric.divmod | head -7"
--------------------------------------------------------- Numeric#divmod
num.divmod( aNumeric ) -> anArray
------------------------------------------------------------------------
Returns an array containing the quotient and modulus obtained by
dividing num by aNumeric. If q, r = x.divmod(y), then

q = floor(float(x)/float(y))
=> true

irb(main):345:0> 1.divmod 0.1
=> [9, 0.1]
irb(main):347:0> 9*0.1+0.1
=> 1.0

:(

you can use Rational instead (or BigDecimal, see below),

irb(main):358:0> x=Rational(1,1)
=> Rational(1, 1)
irb(main):360:0> y=Rational(1,10)
=> Rational(1, 10)
irb(main):361:0> r = x % y
=> Rational(0, 1)
irb(main):362:0> puts r
0
=> nil


# Also, look at the
# following example:
# irb(main):009:0> a += (a % b) if ((a % b) > 0)
# => 1.59999999999999

again, do not put all your trust in floats or in machines for that matter :)

irb(main):344:0> 1.5.divmod 0.1
=> [14, 0.0999999999999999]

this time, let us use BigDecimal.

irb(main):369:0> require 'bigdecimal'
=> true
irb(main):375:0* x=BigDecimal.new "1.5"
=> #<BigDecimal:b7adfacc,'0.15E1',8(8)>
irb(main):376:0> y=BigDecimal.new "0.1"
=> #<BigDecimal:b7adb904,'0.1E0',4(8)>
irb(main):377:0> r = x%y
=> #<BigDecimal:b7ad7dcc,'0.0',4(16)>
irb(main):378:0> puts r
0.0
=> nil
irb(main):379:0> q,r = x.divmod(y)
=> [#<BigDecimal:b7accaa8,'0.15E2',4(12)>, #<BigDecimal:b7accb0c,'0.0',4(16)>]
irb(main):380:0> puts q, r
0.15E2
0.0
=> nil

BigDecimal is powerful, Rational is less heavy.
Your choice though.

kind regards -botp


18 Answers

Morton Goldberg

8/30/2007 5:28:00 AM

0

On Aug 29, 2007, at 10:14 PM, Peña, Botp wrote:

> From: Charlie Lehardy [mailto:charlie.lehardy@gmail.com] :
> # irb(main):001:0> 1 % 0.1
> # => 0.1
> #
> # Shouldn't 1 % 0.1 be 0.0 and not 0.1?
>
> never trust floats in division :)
> ruby tries to be friendly at the expense of more surprises...
>
> irb(main):343:0> system "qri numeric.divmod | head -7"
> ---------------------------------------------------------
> Numeric#divmod
> num.divmod( aNumeric ) -> anArray
> ----------------------------------------------------------------------
> --
> Returns an array containing the quotient and modulus obtained by
> dividing num by aNumeric. If q, r = x.divmod(y), then
>
> q = floor(float(x)/float(y)
>
> irb(main):345:0> 1.divmod 0.1
> => [9, 0.1]

If Numeric#divmod adhered to the above, the result would be [10.0,
0.0], so the result is just plain wrong, not a matter of float
inaccuracy. Proof:

x, y = 1.0, 0.1
(x/y).floor.to_f # => 10.0

> irb(main):347:0> 9*0.1+0.1
> => 1.0

Regards, Morton

Calamitas

8/30/2007 7:40:00 AM

0

On 30/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
> On Aug 29, 2007, at 10:14 PM, Peña, Botp wrote:
>
> > From: Charlie Lehardy [mailto:charlie.lehardy@gmail.com] :
> > # irb(main):001:0> 1 % 0.1
> > # => 0.1
> > #
> > # Shouldn't 1 % 0.1 be 0.0 and not 0.1?
> >
> > never trust floats in division :)
> > ruby tries to be friendly at the expense of more surprises...
> >
> > irb(main):343:0> system "qri numeric.divmod | head -7"
> > ---------------------------------------------------------
> > Numeric#divmod
> > num.divmod( aNumeric ) -> anArray
> > ----------------------------------------------------------------------
> > --
> > Returns an array containing the quotient and modulus obtained by
> > dividing num by aNumeric. If q, r = x.divmod(y), then
> >
> > q = floor(float(x)/float(y)
> >
> > irb(main):345:0> 1.divmod 0.1
> > => [9, 0.1]
>
> If Numeric#divmod adhered to the above, the result would be [10.0,
> 0.0], so the result is just plain wrong, not a matter of float
> inaccuracy. Proof:
>
> x, y = 1.0, 0.1
> (x/y).floor.to_f # => 10.0
>
> > irb(main):347:0> 9*0.1+0.1
> > => 1.0

Mathematically, division goes as follows:

given a dividend n, a divisor d, the quotient q and remainder r are
defined as follows:

n = q * d + r
0 <= r < d

The only number required to be integer to make the solution to the
equations unique is q.

In floating point arithmetic, the first equation is approximately
true, usually in as close a way as possible in a certain sense. The
second equation is strictly adhered to, you can check:

irb(main):001:0> 1.0 % 0.1 < 0.1
=> true

As for the equation given by ri, it's not intended to be evaluated by
Ruby (note how you needed to change where you put the floor function.)
Since 0.1 is represented by a float float(0.1) that is slightly larger
than 0.1, and 1.0 i represented exactly by float(1.0), float(1.0) /
float(0.1) will be slightly smaller than 10 in exact arithmetic,
rounding down gives 9.

Peter

Peña, Botp

8/30/2007 7:47:00 AM

0

From: Morton Goldberg [mailto:m_goldberg@ameritech.net]
# On Aug 29, 2007, at 10:14 PM, Peña, Botp wrote:
# >Returns an array containing the quotient and modulus
# >obtained by dividing num by aNumeric. If q, r = x.divmod(y),
# >then
# > q = floor(float(x)/float(y)
# >
# > irb(main):345:0> 1.divmod 0.1
# > => [9, 0.1]
# > irb(main):347:0> 9*0.1+0.1
# > => 1.0
#
# If Numeric#divmod adhered to the above, the result would be [10.0,
# 0.0], so the result is just plain wrong, not a matter of float
# inaccuracy. Proof:
#
# x, y = 1.0, 0.1
# (x/y).floor.to_f # => 10.0

indeed, that is the effect you are testing. what i'm saying is that,

A. result of q from q,r=x.divmod(y)
may _not (always) guarantee that q == (x/y).floor

B. there are too many results of that kind above, so inferred this
is not plain ruby algo error.
see,

irb(main):069:0> (0.5/0.1).floor
=> 5
irb(main):070:0> 0.5.divmod(0.1)
=> [4, 0.1]
irb(main):071:0> (0.6/0.1).floor
=> 5
irb(main):072:0> 0.6.divmod(0.1)
=> [5, 0.1]
irb(main):073:0> (0.7/0.1).floor
=> 6
irb(main):074:0> 0.7.divmod(0.1)
=> [6, 0.0999999999999999]
irb(main):075:0> (1.2/0.1).floor
=> 11
irb(main):076:0> 1.2.divmod(0.1)
=> [11, 0.0999999999999999]
irb(main):077:0> (1.3/0.1).floor
=> 13
irb(main):078:0> 1.3.divmod(0.1)
=> [12, 0.1]

i checked divmod second value is same as modulo result (fr which i based my previous statement to not trust float division; of course, i may be wrong). so i reckon, ruby gets modulo first, then generate q by q=(num-r)/div. Thus ruby can only guarantee num = q*div + r. (maybe modulo is wrong, and the documentation can be wrong too). i think.

i'm sorry. i tend to generalize. i don't read ruby source. correct me if i'm wrong, pls.

kind regards -botp

Alex Gutteridge

8/30/2007 8:57:00 AM

0

On 30 Aug 2007, at 16:46, Peña, Botp wrote:

> i checked divmod second value is same as modulo result (fr which i
> based my previous statement to not trust float division; of course,
> i may be wrong). so i reckon, ruby gets modulo first, then generate
> q by q=(num-r)/div. Thus ruby can only guarantee num = q*div + r.
> (maybe modulo is wrong, and the documentation can be wrong too). i
> think.
>
> i'm sorry. i tend to generalize. i don't read ruby source. correct
> me if i'm wrong, pls.
>
> kind regards -botp

You are correct I think. In numeric.c, flo_divmod (Float#divmod) and
flo_mod (Float#%) both call flodivmod which calls fmod to generate
the modulo and then calculates q as you say.

Alex Gutteridge

Bioinformatics Center
Kyoto University



Morton Goldberg

8/30/2007 11:23:00 AM

0

On Aug 30, 2007, at 3:39 AM, Calamitas wrote:

> On 30/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
>> On Aug 29, 2007, at 10:14 PM, Peña, Botp wrote:
>>
>>> From: Charlie Lehardy [mailto:charlie.lehardy@gmail.com] :
>>> # irb(main):001:0> 1 % 0.1
>>> # => 0.1
>>> #
>>> # Shouldn't 1 % 0.1 be 0.0 and not 0.1?
>>>
>>> never trust floats in division :)
>>> ruby tries to be friendly at the expense of more surprises...
>>>
>>> irb(main):343:0> system "qri numeric.divmod | head -7"
>>> ---------------------------------------------------------
>>> Numeric#divmod
>>> num.divmod( aNumeric ) -> anArray
>>> --------------------------------------------------------------------
>>> --
>>> --
>>> Returns an array containing the quotient and modulus
>>> obtained by
>>> dividing num by aNumeric. If q, r = x.divmod(y), then
>>>
>>> q = floor(float(x)/float(y)
>>>
>>> irb(main):345:0> 1.divmod 0.1
>>> => [9, 0.1]
>>
>> If Numeric#divmod adhered to the above, the result would be [10.0,
>> 0.0], so the result is just plain wrong, not a matter of float
>> inaccuracy. Proof:
>>
>> x, y = 1.0, 0.1
>> (x/y).floor.to_f # => 10.0
>>
>>> irb(main):347:0> 9*0.1+0.1
>>> => 1.0

I going to be stubborn about this.

> Mathematically, division goes as follows:
>
> given a dividend n, a divisor d, the quotient q and remainder r are
> defined as follows:
>
> n = q * d + r
> 0 <= r < d
>
> The only number required to be integer to make the solution to the
> equations unique is q.

No, that only holds for _integer_ division. For fields (e.g., real
numbers), division is defined as the inverse of multiplication. For
computer floats, which are neither integers nor form a field,
defining a modulo operator is tricky (as I said in previous post).
But it can be done better than Ruby is doing it.

A reasonable definition for a float modulo would be:

def m_mod_n(m, n)
m - n * m.quo(n)
end

With this definition

m_mod_n(1.0, 0.1) # => 0.0

> In floating point arithmetic, the first equation is approximately
> true, usually in as close a way as possible in a certain sense. The
> second equation is strictly adhered to, you can check:
>
> irb(main):001:0> 1.0 % 0.1 < 0.1
> => true
>
> As for the equation given by ri, it's not intended to be evaluated by
> Ruby (note how you needed to change where you put the floor function.)
> Since 0.1 is represented by a float float(0.1) that is slightly larger
> than 0.1, and 1.0 i represented exactly by float(1.0), float(1.0) /
> float(0.1) will be slightly smaller than 10 in exact arithmetic,
> rounding down gives 9.

As your example demonstrates, that's a bad algorithm and should not
be used.

Regards, Morton

Calamitas

8/30/2007 9:56:00 PM

0

On 30/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
> On Aug 30, 2007, at 3:39 AM, Calamitas wrote:
>
> > On 30/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
> >> On Aug 29, 2007, at 10:14 PM, Peña, Botp wrote:
> >>
> >>> From: Charlie Lehardy [mailto:charlie.lehardy@gmail.com] :
> >>> # irb(main):001:0> 1 % 0.1
> >>> # => 0.1
> >>> #
> >>> # Shouldn't 1 % 0.1 be 0.0 and not 0.1?
> >>>
> >>> never trust floats in division :)
> >>> ruby tries to be friendly at the expense of more surprises...
> >>>
> >>> irb(main):343:0> system "qri numeric.divmod | head -7"
> >>> ---------------------------------------------------------
> >>> Numeric#divmod
> >>> num.divmod( aNumeric ) -> anArray
> >>> --------------------------------------------------------------------
> >>> --
> >>> --
> >>> Returns an array containing the quotient and modulus
> >>> obtained by
> >>> dividing num by aNumeric. If q, r = x.divmod(y), then
> >>>
> >>> q = floor(float(x)/float(y)
> >>>
> >>> irb(main):345:0> 1.divmod 0.1
> >>> => [9, 0.1]
> >>
> >> If Numeric#divmod adhered to the above, the result would be [10.0,
> >> 0.0], so the result is just plain wrong, not a matter of float
> >> inaccuracy. Proof:
> >>
> >> x, y = 1.0, 0.1
> >> (x/y).floor.to_f # => 10.0
> >>
> >>> irb(main):347:0> 9*0.1+0.1
> >>> => 1.0
>
> I going to be stubborn about this.
>
> > Mathematically, division goes as follows:
> >
> > given a dividend n, a divisor d, the quotient q and remainder r are
> > defined as follows:
> >
> > n = q * d + r
> > 0 <= r < d
> >
> > The only number required to be integer to make the solution to the
> > equations unique is q.
>
> No, that only holds for _integer_ division. For fields (e.g., real
> numbers), division is defined as the inverse of multiplication. For
> computer floats, which are neither integers nor form a field,
> defining a modulo operator is tricky (as I said in previous post).
> But it can be done better than Ruby is doing it.

That definition works (i.e. it defines q and r uniquely) for real
numbers too, provided that q is required to be integer. But that was
not my point, my point was that the condition that r.abs should be
minimal is not the usual one.

> A reasonable definition for a float modulo would be:
>
> def m_mod_n(m, n)
> m - n * m.quo(n)
> end
>
> With this definition
>
> m_mod_n(1.0, 0.1) # => 0.0

This returns 0.0 for m_mod_n(1.0, 0.3) too. Is that the intention?

> > In floating point arithmetic, the first equation is approximately
> > true, usually in as close a way as possible in a certain sense. The
> > second equation is strictly adhered to, you can check:
> >
> > irb(main):001:0> 1.0 % 0.1 < 0.1
> > => true
> >
> > As for the equation given by ri, it's not intended to be evaluated by
> > Ruby (note how you needed to change where you put the floor function.)
> > Since 0.1 is represented by a float float(0.1) that is slightly larger
> > than 0.1, and 1.0 i represented exactly by float(1.0), float(1.0) /
> > float(0.1) will be slightly smaller than 10 in exact arithmetic,
> > rounding down gives 9.
>
> As your example demonstrates, that's a bad algorithm and should not
> be used.

The ideal for floating point arithmetic is that the operations are
performed in real arithmetic (floating point numbers are a subset of
real numbers, so that's an exact operation), and the result is then
taken to be the floating point number closest to the real result.
That's what the expression given by ri expresses, and I don't think
you can really ask for anything better. In Ruby 0.1 really is
0.10000000000000000555 as that is the floating point number closest to
0.1, and it doesn't fit 10 whole times in 1.0. This has nothing to do
with the algorithm used.

Peter

Morton Goldberg

8/31/2007 5:05:00 PM

0

On Aug 30, 2007, at 5:56 PM, Calamitas wrote:

>> A reasonable definition for a float modulo would be:
>>
>> def m_mod_n(m, n)
>> m - n * m.quo(n)
>> end
>>
>> With this definition
>>
>> m_mod_n(1.0, 0.1) # => 0.0
>
> This returns 0.0 for m_mod_n(1.0, 0.3) too. Is that the intention?

My bad. Confused #quo with #div. Should have written

def m_mod_n(m, n)
m - n * m.div(n)
end

m_mod_n(1.0, 0.1) # => 0.0
m_mod_n(1.0, 0.2) # => 0.0
m_mod_n(1.0, 0.3) # => 0.1
m_mod_n(1.0, 0.4) # => 0.2
m_mod_n(1.0, 0.5) # => 0.0
m_mod_n(1.0, 0.6) # => 0.4
m_mod_n(1.0, 0.7) # => 0.3
m_mod_n(1.0, 0.8) # => 0.2
m_mod_n(1.0, 0.9) # => 0.1

and tested before posting.

<snip>

> I don't think you can really ask for anything better.

The above def suggests I can. I'm not saying it's a plug-in
replacement for %, but it shows that a better answer can be obtained
for the case in question.

> In Ruby 0.1
> really is 0.10000000000000000555 as that is the floating point
> number closest to 0.1 ...

That's true ...

> ... and it doesn't fit 10 whole times in 1.0. This has nothing to do
> with the algorithm used.

... but Ruby appears to think otherwise

sprintf("%.25f", 1.0/0.1) # => "10.0000000000000000000000000"

causing me to conclude something is amiss in the % operator algorithm.

Regards, Morton

Charlie Lehardy

8/31/2007 5:39:00 PM

0

On 8/31/07, Morton Goldberg <m_goldberg@ameritech.net> wrote:
> That's true ...
>
> > ... and it doesn't fit 10 whole times in 1.0. This has nothing to do
> > with the algorithm used.
>
> ... but Ruby appears to think otherwise
>
> sprintf("%.25f", 1.0/0.1) # => "10.0000000000000000000000000"
>
> causing me to conclude something is amiss in the % operator algorithm.

From what I can gather from the source code for the numeric, it is
using C's fmod (from math.h) to do the actual calculation and THAT
function is "broken" (see
http://www.ruby-doc.org/doxygen/1.8.4/numeric_8c-s... for the
source code. Relevant portions are at lines 646 through 705).

However (as I've noted before), modf DOES work and if we were to set
the define for HAVE_FMOD to 0 instead of 1 at compile time, that code
would be used and (from what I can tell) % would work properly.

The more I look at this problem, the more confused I get though.
Sadly, this is my first real experience with floats where I've noticed
that they are not so precise. So I'm still trying to catch up a bit.

Charlie

Calamitas

9/1/2007 12:58:00 AM

0

On 31/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
> On Aug 30, 2007, at 5:56 PM, Calamitas wrote:
> > I don't think you can really ask for anything better.
>
> The above def suggests I can. I'm not saying it's a plug-in
> replacement for %, but it shows that a better answer can be obtained
> for the case in question.

But what is better? I think we disagree on that.

> > In Ruby 0.1
> > really is 0.10000000000000000555 as that is the floating point
> > number closest to 0.1 ...
>
> That's true ...
>
> > ... and it doesn't fit 10 whole times in 1.0. This has nothing to do
> > with the algorithm used.
>
> ... but Ruby appears to think otherwise
>
> sprintf("%.25f", 1.0/0.1) # => "10.0000000000000000000000000"
>
> causing me to conclude something is amiss in the % operator algorithm.

But that is a different question...

It seems to me that you are looking for a way to make % and / obey a
mathematical equation that is true for reals but that simply isn't for
floats. You can change the definitions of % and / slightly to obey the
rule you want it to obey (although I'm not entirely sure that your
"fix" works in general,) but you can't change it to satisfy all
mathematical equations that are true for reals, and any change
probably makes other calculations more imprecise.

I can understand your feeling though. You expect 1.0 % 0.1 to be 0.0
and you get 0.1, which is a big difference, a lot bigger than the one
between 0.1 and 0.10000000000000000555. This is a consequence of the
fact that % is a discontinuous function. Any discontinuous function is
dangerous in floating point arithmetic, but only when used near its
discontinuities. The only general solution to this is either not to
use discontinuous functions near their discontinuities, or take into
account the large imprecision. This is really a well-understood
problem. It's not the algorithm used that is the problem, it's the
question asked that is the problem.

Regards,
Peter

Morton Goldberg

9/1/2007 2:09:00 AM

0

On Aug 31, 2007, at 8:57 PM, Calamitas wrote:

> On 31/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
>> On Aug 30, 2007, at 5:56 PM, Calamitas wrote:
>>> I don't think you can really ask for anything better.
>>
>> The above def suggests I can. I'm not saying it's a plug-in
>> replacement for %, but it shows that a better answer can be obtained
>> for the case in question.
>
> But what is better? I think we disagree on that.

When there are two methods for performing a computation, the better
one is the one that produces the more accurate result. When floats
are involved that means the method which produces the result closer
to what one would get with the real numbers they are meant to imitate.

>>> In Ruby 0.1
>>> really is 0.10000000000000000555 as that is the floating point
>>> number closest to 0.1 ...
>>
>> That's true ...
>>
>>> ... and it doesn't fit 10 whole times in 1.0. This has nothing to do
>>> with the algorithm used.
>>
>> ... but Ruby appears to think otherwise
>>
>> sprintf("%.25f", 1.0/0.1) # => "10.0000000000000000000000000"
>>
>> causing me to conclude something is amiss in the % operator
>> algorithm.
>
> But that is a different question.

It's the question I'm focused on, and the question I believe the OP
was bringing up.

> It seems to me that you are looking for a way to make % and / obey a
> mathematical equation that is true for reals but that simply isn't for
> floats. You can change the definitions of % and / slightly to obey the
> rule you want it to obey (although I'm not entirely sure that your
> "fix" works in general,) but you can't change it to satisfy all
> mathematical equations that are true for reals, and any change
> probably makes other calculations more imprecise.

I'm only talking about % here. I don't think / is an issue. I think
issue revolves around the discontinuity of % and the inability of a
binary floating-point number system to represent numbers such as 1/10
exactly.

> I can understand your feeling though. You expect 1.0 % 0.1 to be 0.0
> and you get 0.1, which is a big difference, a lot bigger than the one
> between 0.1 and 0.10000000000000000555. This is a consequence of the
> fact that % is a discontinuous function.

The discontinuity of % is certainly at the root of the problem, but ...

> Any discontinuous function is
> dangerous in floating point arithmetic, but only when used near its
> discontinuities. The only general solution to this is either not to
> use discontinuous functions near their discontinuities, or take into
> account the large imprecision. This is really a well-understood
> problem. It's not the algorithm used that is the problem, it's the
> question asked that is the problem.

...this is where we differ. I believe the % algorithm should take the
points of discontinuity into account and tread very carefully in
their neighborhoods.

Regards, Morton