For Programmers: Free Programming Magazines  


Home > Archive > PERL Miscellaneous > September 2005 > puzzling behavior in simple variable comparison









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author puzzling behavior in simple variable comparison
aclarke@austin.rr.com

2005-09-26, 9:56 pm

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. Sinan Unur

2005-09-26, 9:56 pm

aclarke@austin.rr.com 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 <1usa@llenroc.ude.invalid>
(reverse each component and remove .invalid for email address)

comp.lang.perl.misc guidelines on the WWW:
http://mail.augustmail.com/~tadmc/c...guidelines.html
A. Sinan Unur

2005-09-26, 9:56 pm

"A. Sinan Unur" <1usa@llenroc.ude.invalid> wrote in
news:Xns96DDD40E316E5asu1cornelledu@127.0.0.1:

> aclarke@austin.rr.com wrote in news:1127779631.702662.169370
> @o13g2000cwo.googlegroups.com:
>
>
> 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/I...CM/goldberg.pdf).


--
A. Sinan Unur <1usa@llenroc.ude.invalid>
(reverse each component and remove .invalid for email address)

comp.lang.perl.misc guidelines on the WWW:
http://mail.augustmail.com/~tadmc/c...guidelines.html
Dr.Ruud

2005-09-26, 9:56 pm

aclarke@austin.rr.com 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."


A. Sinan Unur

2005-09-26, 9:56 pm

"Dr.Ruud" <rvtol+news@isolution.nl> wrote in news:dhacqc.1i4.1@news.isolution.nl:

> aclarke@austin.rr.com schreef:
>
>
> 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/pape...aringfloats.htm

Sinan
--
A. Sinan Unur <1usa@llenroc.ude.invalid>
(reverse each component and remove .invalid for email address)

comp.lang.perl.misc guidelines on the WWW:
http://mail.augustmail.com/~tadmc/c...guidelines.html
aclarke@austin.rr.com

2005-09-26, 9:56 pm

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

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com