# 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

2. ### A. Sinan UnurGuest

wrote in news:1127779631.702662.169370

> 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:

A. Sinan Unur, Sep 27, 2005

3. ### A. Sinan UnurGuest

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

> wrote in news:1127779631.702662.169370
>
>> 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...

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:

A. Sinan Unur, Sep 27, 2005
4. ### Dr.RuudGuest

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
5. ### A. Sinan UnurGuest

"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]);
>

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:

A. Sinan Unur, Sep 27, 2005
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