Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Whose fault is my problem?

Reply
Thread Tools

Whose fault is my problem?

 
 
The Podman
Guest
Posts: n/a
 
      08-04-2008
I've recently started to write an appliaction in Ruby. It's the first
time I'm using the language. Infact, the only reason I'm using it is
because it's all my friends EEE laptop had on it at the time (What?!
No gcc!!?) Still, I've started now so I'll stick with it. Anyway, after
reading the the pickaxe book from cover to cover, I decided to change my
unit tests from a bunch of "puts blahblah, stuff" to those lovely
Test::Unit modules..... and then I encountered a problem.

As some point, I'm asking Ruby to assert_equal(a,b), where a and b are
Vectors (from the matrix.rb file). Unfortunatley one test fails,
specifically in the case where Vector[27.8, 0.0] does not equal
Vector[27.8, 0.0]. After much head scratching and playing about with the
debugger, I realised it's the age old problem of floating point
comparison. For some reason I just didn't expect to have to worry about
this in Ruby! Annoyingly, I don't know how to go about "fixing" my
problem. and my problem is this:

* I want to unit test.
* I want to use assert_equal, because I'm using it for the other 20 or
so tests called by the same looping iterator and it works just fine for
these :}
* Unfortunatley assert_equals calls == on the two objects in question.
* These two objects (in this case) are Vectors. Vector == Vector simply
hands of equality checking to array's == method, asking if Vector.array
== Vector.array.
* Array == iterates though both arrays and uses == between the elements.
* Float == Float does not perform any delta difference checking. In a
way, this is expected. In another way, it's not.

So who is to blame? Me, for not realising that the objects I'm trying to
compare have two floats somewhere in some array and that I should
descend into these objects and manually compare their internal data?
Whilst in this case it's rather explicit where the problem is, as it's
just a math's style Vector of Floats, in most cases I won't know the
implementation details of the objects, I'm dealing with, right? And if
those objects happened to bring in a Floating point number somewhere,
how am I to know that a != b purely because of this issue?

Or is it the fault of Vector, for not realising that it'll often have
floating point numbers stuffed into it and that it should do something a
bit cleverer that array == array?

Or is it the fault of Float, for not realising that Ruby programmers
shouldn't have to care about trivial things like two floats being
0.00000003e-17 out?

When I get home I'm going to have to make my unit tests care about delta
difference between two floating point numbers (infact, there's a method
to help me do that...), but annoyingly I'm going to have to start
plucking data out of an object and essentially re-implement == between
two Vectors. Whilst this will fix my problem in this case, I imagine it
won't fix other people's problems in the general case. So, Ruby fans,
what should be done? Should this problem exist in a language like Ruby?
Or not? After all, if it quacks like a 27.8 and displays like a 27.8,
shouldn't Ruby treat it as 27.8?

Cheers,
Pod.

ps, you can emulate the problem with a simple bit of "p 37.8 - 10 ==
27.8" -> false.
--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
 
 
 
Robert Klemme
Guest
Posts: n/a
 
      08-04-2008
2008/8/4 The Podman <(E-Mail Removed)>:

[Vectors with floats in them are not equal according to ==.]

> So who is to blame? Me, for not realising that the objects I'm trying to
> compare have two floats somewhere in some array and that I should
> descend into these objects and manually compare their internal data?


I am afraid, yes.

> Whilst in this case it's rather explicit where the problem is, as it's
> just a math's style Vector of Floats, in most cases I won't know the
> implementation details of the objects, I'm dealing with, right? And if
> those objects happened to bring in a Floating point number somewhere,
> how am I to know that a != b purely because of this issue?
>
> Or is it the fault of Vector, for not realising that it'll often have
> floating point numbers stuffed into it and that it should do something a
> bit cleverer that array == array?


No, because a Vector cannot make any assumptions about when you as a
user would consider two floats equivalent (i.e. what is the maximum
delta you would accept - or is it a factor? Does it depend on the
number's sizes etc.).

> Or is it the fault of Float, for not realising that Ruby programmers
> shouldn't have to care about trivial things like two floats being
> 0.00000003e-17 out?


Same here, Float cannot know what your requirements are.

In light of this for library code it is the most reasonable thing to
implement == as binary identity for floats.

> When I get home I'm going to have to make my unit tests care about delta
> difference between two floating point numbers (infact, there's a method
> to help me do that...), but annoyingly I'm going to have to start
> plucking data out of an object and essentially re-implement == between
> two Vectors.


Depending on the lib you might as well be able to calculate the abs
difference of the two Vectors and assert that the result is less than
a Vector with all deltas that you define as constant somewhere.

> Whilst this will fix my problem in this case, I imagine it
> won't fix other people's problems in the general case. So, Ruby fans,
> what should be done? Should this problem exist in a language like Ruby?


Unfortunately there is no general one size fits all solution when it
comes to floating point number equivalence. This is the same for all
languages - apart from algebraic tools such as Mathematica which work
quite different internally.

> Or not? After all, if it quacks like a 27.8 and displays like a 27.8,
> shouldn't Ruby treat it as 27.8?


Well it does, but "27.8" might not be the full truth. You need to
keep in mind that "27.8" is a _decimal representation_ while
internally the number is stored in a _binary representation_.
Conversion between the two introduces errors which is the reason for
the phenomena you (and a lot others) are seeing.

For results that resemble more closely what you would expect you could
use BigDecimal.

17:02:08 bas$ irb -r bigdecimal
Ruby version 1.8.7
irb(main):001:0> a = BigDecimal.new '37.8'
=> #<BigDecimal:7ff91634,'0.378E2',8(>
irb(main):002:0> b = BigDecimal.new '27.8'
=> #<BigDecimal:7ff88084,'0.278E2',8(>
irb(main):003:0> a - b
=> #<BigDecimal:7ff813b0,'0.1E2',4(16)>
irb(main):004:0> (a - b).to_s
=> "0.1E2"
irb(main):005:0> (a - b) == 10
=> true
irb(main):006:0> "%10.3f" % (a - b)
=> " 10.000"
irb(main):007:0> "%.3f" % (a - b)
=> "10.000"

Kind regards

robert

--
use.inject do |as, often| as.you_can - without end

 
Reply With Quote
 
 
 
 
William Rutiser
Guest
Posts: n/a
 
      08-04-2008
The Podman wrote:
> As some point, I'm asking Ruby to assert_equal(a,b), where a and b are
> Vectors (from the matrix.rb file). Unfortunatley one test fails,
> specifically in the case where Vector[27.8, 0.0] does not equal
> Vector[27.8, 0.0]. After much head scratching and playing about with the
> debugger, I realised it's the age old problem of floating point
> comparison. For some reason I just didn't expect to have to worry about
> this in Ruby! Annoyingly, I don't know how to go about "fixing" my
> problem. and my problem is this:
>

You may find something like this useful. Its based on the tolerant
compare function used in APL where it is used as the definition of
equality for floating point numbers. Note that in APL fuzz is actually a
special global that can be set as necessary to change the relative
tolerance. Note that this is quick and dirty code and does not handle
the special case when one of the arguments is zero. This value of fuzz
would be appropriate for "equality within 10 decimal places".

Ruby has a variety of means to extend or modify assert_equal to work
this way.

--------------------------------

def fuzzy_equals( x, y )
fuzz = 1.0E-10
max = [x.abs, y.abs].max
( x - y ).abs / max < fuzz
end

class TC_Fuzzy < Test::Unit::TestCase
def test_01
assert( fuzzy_equals( 17 + 1.0E-10, 17 ) )
assert( fuzzy_equals( 1.7 + 1.0E-10, 1.7 ) )
assert( ! fuzzy_equals( 0.7 + 1.0E-10, 0.7 ) )

assert( fuzzy_equals( 17 - 1.0E-10, 17 ) )
assert( fuzzy_equals( 1.7 - 1.0E-10, 1.7 ) )
assert( ! fuzzy_equals( 0.7 - 1.0E-10, 0.7 ) )

assert( fuzzy_equals( -17 + 1.0E-10, -17 ) )
assert( fuzzy_equals( -1.7 + 1.0E-10, -1.7 ) )
assert( ! fuzzy_equals( -0.7 + 1.0E-10, -0.7 ) )

assert( fuzzy_equals( -17 - 1.0E-10, -17 ) )
assert( fuzzy_equals( -1.7 - 1.0E-10, -1.7 ) )
assert( ! fuzzy_equals( -0.7 - 1.0E-10, -0.7 ) )
end
end

----------------------------------

Bill Rutiser

 
Reply With Quote
 
Gregory Brown
Guest
Posts: n/a
 
      08-04-2008
On Mon, Aug 4, 2008 at 11:50 AM, William Rutiser <(E-Mail Removed)> wrote:

> Ruby has a variety of means to extend or modify assert_equal to work this
> way.


Test::Unit actually has an assertion for this, better to use it than
roll your own:

require "test/unit"

class FooTest < Test::Unit::TestCase

def test_in_delta
assert_in_delta 17.001, 17.00100001, 0.0001
end

end

---
Killer Ruby PDF Generation named after a magnificent sea creature:
http://github.com/sandal/prawn | Non-tech stuff at:
http://metametta.blogspot.com

 
Reply With Quote
 
Dejan Dimic
Guest
Posts: n/a
 
      08-04-2008
On Aug 4, 6:23*pm, Gregory Brown <(E-Mail Removed)> wrote:
> On Mon, Aug 4, 2008 at 11:50 AM, William Rutiser <(E-Mail Removed)>wrote:
> > Ruby has a variety of means to extend or modify assert_equal to work this
> > way.

>
> Test::Unit actually has an assertion for this, better to use it than
> roll your own:
>
> require "test/unit"
>
> class FooTest < Test::Unit::TestCase
>
> * def test_in_delta
> * * assert_in_delta 17.001, 17.00100001, 0.0001
> * end
>
> end
>
> ---
> Killer Ruby PDF Generation named after a magnificent sea creature:http://github.com/sandal/prawn| Non-tech stuff at:http://metametta.blogspot.com


Just to add that this is an universal programming issue present in all
programming languages that deals with floats without any assumptions.
You should get used to it and always kip in mind when using floats,
doubles, double precision or currency data types even some date and
time data types that internally stored as double or float.

That is one of the small things that make our lives more
interesting.
 
Reply With Quote
 
Daniel Moore
Guest
Posts: n/a
 
      08-04-2008
On Mon, Aug 4, 2008 at 7:38 AM, The Podman <(E-Mail Removed)> wrote:

> * Float == Float does not perform any delta difference checking. In a
> way, this is expected. In another way, it's not.


If you don't like the == method of Float just crack it open and change it:

a = 12.60000000000001
b = 12.59999999999999

puts a
puts b

puts a == b # => false

class Float
def ==(other)
fuzz = 1.0E-10
max = [self.abs, other.abs].max
( self - other ).abs / max < fuzz
end
end

puts a == b # => true

I would only recommend doing this for your tests though, other code
might want the original == method intact.


--

-Daniel

 
Reply With Quote
 
Stefan Lang
Guest
Posts: n/a
 
      08-04-2008
2008/8/4 Daniel Moore <(E-Mail Removed)>:
> On Mon, Aug 4, 2008 at 7:38 AM, The Podman <(E-Mail Removed)> wrote:
>
>> * Float == Float does not perform any delta difference checking. In a
>> way, this is expected. In another way, it's not.

>
> If you don't like the == method of Float just crack it open and change it:
>
> a = 12.60000000000001
> b = 12.59999999999999
>
> puts a
> puts b
>
> puts a == b # => false
>
> class Float
> def ==(other)
> fuzz = 1.0E-10
> max = [self.abs, other.abs].max
> ( self - other ).abs / max < fuzz
> end
> end
>
> puts a == b # => true
>
> I would only recommend doing this for your tests though, ...


I think that's a bad strategy. If he has == comparisons of Float
in his code where a delta check would be the right thing,
this would hide it from the tests.

Stefan

 
Reply With Quote
 
Lee Griffiths
Guest
Posts: n/a
 
      08-05-2008
Di Mo wrote:
> On Mon, Aug 4, 2008 at 7:38 AM, The Podman <(E-Mail Removed)> wrote:
>
>> * Float == Float does not perform any delta difference checking. In a
>> way, this is expected. In another way, it's not.

>
> If you don't like the == method of Float just crack it open and change
> it:
>
> a = 12.60000000000001
> b = 12.59999999999999
>
> puts a
> puts b
>
> puts a == b # => false
>
> class Float
> def ==(other)
> fuzz = 1.0E-10
> max = [self.abs, other.abs].max
> ( self - other ).abs / max < fuzz
> end
> end
>
> puts a == b # => true
>
> I would only recommend doing this for your tests though, other code
> might want the original == method intact.
>
>
> --
>
> -Daniel



I did just that for this project. Well, not exactly that, but
something similiar (I copied and pasted it from an old C file I had). I
still have the old == aliased as "old_equ". Infact my new equ method
uses it as an inital check.

I'm used to dealing with floats. I _know_ the ways in which they should
be compared. However, right now I don't require my points to be deadly
accurate. I'll be happy just as long as they're floating. I also don't
see Ruby being used for precise mathmatical simulations (even though
that's what I'm using it for. Erk!) and think it should have some built
in support for delta-comparison. After all, if it's happy enough to
display something as "2.85", why can't it just treat it like 2.85?*
Sometimes that 0.3e-27 really, really isn't important and should just
/go away/ and stop ruining things

My main issue that sparked the first post was that so many things will
just assume == gives the correct result. It does give the correct
result, but my problem is when they're imbedded deep inside two objects
that are compared to each other using ==, and these objects just naivey
ask all their internal attributes if they == other.attribute . So why
doesn't Ruby have, by default:

a) a built in =~ or Float#approx(other) method?
b) The ability to "switch on" and "off" wether == or =~ is used whenever
== is called (I hope that makes sense!)?

Whilst for now my extensions to Float will do (for me, at least), I
think that the default Ruby float class should deal with this issue
without having to drag up BigDecimal. What do you all think? Is built in
fuzzy checking, and the option to use it on or off to much to ask?

* Of course, to many people the 0.3e-27 difference is very important,
but those people could turn off the fuzzy checking
--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-06-2008
2008/8/5 Lee Griffiths <(E-Mail Removed)>:
> My main issue that sparked the first post was that so many things will
> just assume == gives the correct result. It does give the correct
> result, but my problem is when they're imbedded deep inside two objects
> that are compared to each other using ==, and these objects just naivey
> ask all their internal attributes if they == other.attribute . So why
> doesn't Ruby have, by default:
>
> a) a built in =~ or Float#approx(other) method?
> b) The ability to "switch on" and "off" wether == or =~ is used whenever
> == is called (I hope that makes sense!)?
>
> Whilst for now my extensions to Float will do (for me, at least), I
> think that the default Ruby float class should deal with this issue
> without having to drag up BigDecimal. What do you all think? Is built in
> fuzzy checking, and the option to use it on or off to much to ask?


I believe the problem with this is its high potential for catastrophe:
assume you have a bit of code that uses a library which in turn uses
another lib. You switch fuzzy on but it is not selective, i.e. will
affect all comparisons - in both libs, one of them you might not even
be aware of using. Code written with the assumption that the flag is
switched off will go awry.

After all, there is a standard and I believe it is a bad idea to allow
standard classes to deviate from that. And embedding the flag switch
in code that requires standard behavior will clutter it quite a lot.

IMHO the proper solution would be to craft your own float math class
that just wraps a Float and implements == the way you like while
otherwise behaving as float. I would not mess with Float#== not even
temporarily.

Kind regards

robert


--
use.inject do |as, often| as.you_can - without end

 
Reply With Quote
 
Dave Bass
Guest
Posts: n/a
 
      08-06-2008
I think it may be best to regard floats conceptually as reals.
Mathematics tells us that almost all reals are irrational and almost
none of them are rational. That means that almost every comparison of
the form x == 2.85 (where the LHS is real and the RHS is rational) will
be false.

Therefore the best guidance, often seen in Programming 101, is "Never
compare floats for equality!" The only numbers that are guaranteed to be
exact in a computer are integers, so one should only compare integers
for equality.

Of course in a computer we're always dealing with rational numbers (due
to finite precision) so this isn't quite true, but it's a good guiding
principle.

Using a fuzzy comparison dressed up as an exact comparison is a bad idea
IMHO. If you want to do that, do it explicitly, something like
assert_in_delta.

In many years of numerical simulation work (Fortran, C, Perl, ...) I've
never had to compare floats for equality. There has always been a better
way to do the job.

Dave

--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Gridview with combobox whose data is dependent on field in gridview wallermj@hotmail.com ASP .Net 3 11-21-2005 05:34 PM
Whose the youngest in India? =?Utf-8?B?U2F0aXNo?= MCSD 9 05-30-2005 02:54 AM
Error *cannot return a value from method whose result type is void* Charles Java 6 07-28-2004 12:13 PM
Stack fault and page fault help S.Flournoy Computer Support 2 04-17-2004 04:23 PM
PSU fried drive, but whose fault was it? What now? Damon Malkovich Computer Support 6 04-08-2004 10:56 AM



Advertisments