Whose fault is my problem?

Discussion in 'Ruby' started by The Podman, Aug 4, 2008.

  1. The Podman

    The Podman Guest

    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/.
     
    The Podman, Aug 4, 2008
    #1
    1. Advertising

  2. 2008/8/4 The Podman <>:

    [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(8)>
    irb(main):002:0> b = BigDecimal.new '27.8'
    => #<BigDecimal:7ff88084,'0.278E2',8(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
     
    Robert Klemme, Aug 4, 2008
    #2
    1. Advertising

  3. 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
     
    William Rutiser, Aug 4, 2008
    #3
  4. On Mon, Aug 4, 2008 at 11:50 AM, William Rutiser <> 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
     
    Gregory Brown, Aug 4, 2008
    #4
  5. The Podman

    Dejan Dimic Guest

    On Aug 4, 6:23 pm, Gregory Brown <> wrote:
    > On Mon, Aug 4, 2008 at 11:50 AM, William Rutiser <>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. :)
     
    Dejan Dimic, Aug 4, 2008
    #5
  6. The Podman

    Daniel Moore Guest

    On Mon, Aug 4, 2008 at 7:38 AM, The Podman <> 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
     
    Daniel Moore, Aug 4, 2008
    #6
  7. The Podman

    Stefan Lang Guest

    2008/8/4 Daniel Moore <>:
    > On Mon, Aug 4, 2008 at 7:38 AM, The Podman <> 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
     
    Stefan Lang, Aug 4, 2008
    #7
  8. Di Mo wrote:
    > On Mon, Aug 4, 2008 at 7:38 AM, The Podman <> 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/.
     
    Lee Griffiths, Aug 5, 2008
    #8
  9. 2008/8/5 Lee Griffiths <>:
    > 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
     
    Robert Klemme, Aug 6, 2008
    #9
  10. The Podman

    Dave Bass Guest

    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/.
     
    Dave Bass, Aug 6, 2008
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    3
    Views:
    5,116
  2. Charles
    Replies:
    6
    Views:
    13,961
  3. Luca Rosellini
    Replies:
    6
    Views:
    728
    John C. Bollinger
    Nov 22, 2004
  4. puzzlecracker
    Replies:
    2
    Views:
    5,133
    Jean-Francois Briere
    Dec 20, 2005
  5. slimzhao
    Replies:
    1
    Views:
    413
    Jens M. Felderhoff
    Sep 28, 2003
Loading...

Share This Page