If this channel is not a proper way to report such kind of Perl bug, I will
say "sorry", and please direct to a more proper channel and inform me.
Many thanks.
==========================================================================
In way to truncate (not round) a number to 2 decimal places, I found that
the following Perl code yields wrong output (I expect 1.15 as the result).
Is it caused by improper rounding handling of Perl? Has it been documented
somewhere in sites of Perl?
Code: ---------- ---------- ---------- ---------- ----------
#!/usr/local/public/perl5.8.6/bin/perl -w
print "----- string decimal -----\n";
print int('1.15' * 100) / 100 ."\n";
print "----- numeric decimal -----\n";
print int(1.15 * 100) / 100 ."\n";
Result: ---------- ---------- ---------- ---------- ----------
----- string decimal -----
1.14
----- numeric decimal -----
1.14
To quickly solve the problem, I write the following code. It works but I
would like to know the most elegant way to do the job. Could you please
suggest?
Code: ---------- ---------- ---------- ---------- ----------
#!/usr/local/public/perl5.8.6/bin/perl -w
# truncate $pNum to 2 decimal places
sub trunc {
my ($pNum) = @_;
my $Num2DecPlace;
my $idx = index($pNum, '.');
if ( $idx > 0 ) {
$Num2DecPlace = substr($pNum, 0, $idx+3);
} else {
$Num2DecPlace = $pNum;
}
return $Num2DecPlace;
}
#
print "----- -----\n";
print trunc('987') ."\n";
print trunc('1.15') ."\n";
print trunc('1.1491') ."\n";
print trunc('1.1401') ."\n";
print trunc('1.1') ."\n";
print "----- -----\n";
print trunc(987) ."\n";
print trunc(1.15) ."\n";
print trunc(1.1491) ."\n";
print trunc(1.1401) ."\n";
print trunc(1.1) ."\n";
Result: ---------- ---------- ---------- ---------- ----------
----- -----
987
1.15
1.14
1.14
1.1
----- -----
987
1.15
1.14
1.14
1.1
> If this channel is not a proper way to report such kind of Perl bug, I will
> say "sorry", and please direct to a more proper channel and inform me.
> Many thanks.
It is the correct way to report a bug. However, your program appears to
exhibit documented (if confusing) behavior.
> ==========================================================================
>
>
> In way to truncate (not round) a number to 2 decimal places, I found that
> the following Perl code yields wrong output (I expect 1.15 as the result).
> Is it caused by improper rounding handling of Perl? Has it been documented
> somewhere in sites of Perl?
This appears to be the well-known imprecision of using floating point numbers
in binary machines, as documented in perlfaq4:
Why am I getting long decimals (eg, 19.9499999999999) instead of the
numbers I should be getting (eg, 19.95)?
Internally, your computer represents floating-point numbers in binary.
Digital (as in powers of two) computers cannot store all numbers
exactly. Some real numbers lose precision in the process. This is a
problem with how computers store numbers and affects all computer lan-
guages, not just Perl.
> To quickly solve the problem, I write the following code. It works but I
> would like to know the most elegant way to do the job. Could you please
> suggest?
sprintf() or printf() with the appropriate format will do the trick:
To limit the number of decimal places in your numbers, you can use the
printf or sprintf function. See the "Floating Point Arithmetic" for
more details.
printf "%.2f", 10/3;
my $number = sprintf "%.2f", 10/3;
I rewrote your trunc() as:
sub trunc
{
my ($pNum) = @_;
return $pNum unless $pNum =~ /\./;
return sprintf( '%.2f', $pNum );
}
... and received the same results you describe.
-- c
But that rounds, not truncates. To truncate (zeroward) while allowing
a little wiggle room for non-exact representation, increase by a small
amount (e.g. multiply by 1+1e-15) before doing the *100, int, /100
thing.
> But that rounds, not truncates. To truncate (zeroward) while allowing
> a little wiggle room for non-exact representation, increase by a small
> amount (e.g. multiply by 1+1e-15) before doing the *100, int, /100
> thing.
Oops, yes... you're right.
-- c