puzzling behavior in simple variable comparison

Discussion in 'Perl Misc' started by aclarke@austin.rr.com, Sep 27, 2005.

  1. Guest

    Greetings!

    I have been doing Perl for about 3 years now and have run across this
    puzzling behavior in the code I wrote. Why doesn't the program print 0
    as the value from the comparison? The debugger
    shows the values as the same!

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

    use strict;

    # A BigEpoch is a pair of numbers that are a restricted attempt to
    # get larger fixed point arithmetic than is built into Perl. It
    consists
    # of a high part and a low part. Normally the high part is an
    # integer and the low part is a fraction (the idea is that the high
    part
    # will represent epoch seconds and the low part milli or microseconds).
    # An example value is [30.0, 0.12345], which represents the value
    # 30.12345

    # A BigEpoch can get sloppy due to calculations; it needs to be
    normalized
    # so that the high number is integral and the low is less than one (a
    # fraction).
    #
    sub BigEpochNormalize
    {
    my ($bigepoch) = @_;

    my $int0 = int($$bigepoch[0]);
    my $int1 = int($$bigepoch[1]);

    my $frac0 = $$bigepoch[0] - $int0;
    my $frac1 = $$bigepoch[1] - $int1;

    $int0 += $int1;
    $frac0 += $frac1;

    $int0 += int($frac0);
    $frac0 -= int($frac0);

    # need to handling borrowing in either direction

    if ($int0 > 0 && $frac0 < 0)
    {
    $int0 -= 1;
    $frac0 += 1;
    }
    elsif ($int0 < 0 && $frac0 > 0)
    {
    $int0 += 1;
    $frac0 -= 1;
    }

    $$bigepoch[0] = $int0;
    $$bigepoch[1] = $frac0;

    return $bigepoch;
    }

    # This is a cmp function that works against BigEpochs
    #
    sub BigEpochCmp
    {
    my ($bigepoch1,$bigepoch2) = @_;

    my $part1 = ($$bigepoch1[0] <=> $$bigepoch2[0]);
    return $part1 if ($part1);

    # high part is equals, so compare the low part

    my $part2 = ($$bigepoch1[1] <=> $$bigepoch2[1]);
    return $part2;
    }

    my $input = [ 1.1, 0.1 ];
    my $output = BigEpochNormalize ($input);
    my $expected = [ 1.0, 0.2 ];

    print "The value below should be 0 (meaning equals)\n";
    print "Cmp is ", BigEpochCmp($output, $expected), "\n";
    , Sep 27, 2005
    #1
    1. Advertising

  2. wrote in news:1127779631.702662.169370
    @o13g2000cwo.googlegroups.com:

    > my $expected = [ 1.0, 0.2 ];


    I don't think 0.2 can be represented exactly in binary floating point. I
    think its expansion is 0.001100110011...

    Sinan

    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
    A. Sinan Unur, Sep 27, 2005
    #2
    1. Advertising

  3. "A. Sinan Unur" <> wrote in
    news:Xns96DDD40E316E5asu1cornelledu@127.0.0.1:

    > wrote in news:1127779631.702662.169370
    > @o13g2000cwo.googlegroups.com:
    >
    >> my $expected = [ 1.0, 0.2 ];

    >
    > I don't think 0.2 can be represented exactly in binary floating point.
    > I think its expansion is 0.001100110011...


    Indeed. Add the following lines to your script:

    printf "%.18f\n", $_ for @{ $output };
    printf "%.18f\n", $_ for @{ $expected };

    D:\Home\asu1\UseNet\clpmisc> z
    1.000000000000000000
    0.200000000000000090
    1.000000000000000000
    0.200000000000000010
    The value below should be 0 (meaning equals)
    Cmp is 1

    This, by the way, is a frequently asked question:

    perldoc -q .999

    Sinan

    PS: The classic article on this topic is "What Every Computer Scientist
    Should Know About Floating Point Arithmetic" (see:
    http://citeseer.ist.psu.edu/goldberg91what.html or
    http://cch.loria.fr/documentation/IEEE754/ACM/goldberg.pdf).


    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
    A. Sinan Unur, Sep 27, 2005
    #3
  4. Dr.Ruud Guest

    schreef:

    > sub BigEpochCmp
    > {
    > my ($bigepoch1,$bigepoch2) = @_;
    >
    > my $part1 = ($$bigepoch1[0] <=> $$bigepoch2[0]);
    > return $part1 if ($part1);
    >
    > # high part is equals, so compare the low part
    >
    > my $part2 = ($$bigepoch1[1] <=> $$bigepoch2[1]);
    > return $part2;
    > }


    You need to limit the acceptable difference, for example:

    $part2 = (abs($$bigepoch1[1] - $$bigepoch2[1]) < 1e-14)
    ? 0
    : ($$bigepoch1[1] <=> $$bigepoch2[1]);

    --
    Affijn, Ruud

    "Gewoon is een tijger."
    Dr.Ruud, Sep 27, 2005
    #4
  5. "Dr.Ruud" <> wrote in news::

    > schreef:
    >
    >> sub BigEpochCmp
    >> {
    >> my ($bigepoch1,$bigepoch2) = @_;
    >>
    >> my $part1 = ($$bigepoch1[0] <=> $$bigepoch2[0]);
    >> return $part1 if ($part1);
    >>
    >> # high part is equals, so compare the low part
    >>
    >> my $part2 = ($$bigepoch1[1] <=> $$bigepoch2[1]);
    >> return $part2;
    >> }

    >
    > You need to limit the acceptable difference, for example:
    >
    > $part2 = (abs($$bigepoch1[1] - $$bigepoch2[1]) < 1e-14)
    > ? 0
    > : ($$bigepoch1[1] <=> $$bigepoch2[1]);
    >


    See also:

    http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

    Sinan
    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
    A. Sinan Unur, Sep 27, 2005
    #5
  6. Guest

    Thanks both for replying. I can't believe that I got bitten by this.
    Its a pretty common occurrance. I guess I was tired and believed the
    debugger when it was showing me .2 as input and .2 as output. I even
    "converted" them to strings and cmp showed them the same when <=> did
    not.

    Arg. I won't even tell you how long I have been programming either...

    Allan
    , Sep 27, 2005
    #6
    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. GIMME
    Replies:
    1
    Views:
    953
    Chris Smith
    Apr 15, 2004
  2. Replies:
    3
    Views:
    275
  3. Replies:
    0
    Views:
    268
  4. Jav
    Replies:
    3
    Views:
    142
  5. VK
    Replies:
    4
    Views:
    143
Loading...

Share This Page