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