puzzling behavior in simple variable comparison

A

aclarke

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";
 
A

A. Sinan Unur

(e-mail address removed) wrote in @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

A. Sinan Unur

(e-mail address removed) wrote in @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).
 
D

Dr.Ruud

(e-mail address removed) 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]);
 
A

A. Sinan Unur

Dr.Ruud said:
(e-mail address removed) 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

aclarke

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top