Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[perl #38434] [Bug/Perl5.8.6] wrong Perl decimal output; find the best way to truncate decimal place

0 views
Skip to first unread message

LY Lam

unread,
Feb 5, 2006, 1:43:14 AM2/5/06
to bugs-bi...@rt.perl.org
# New Ticket Created by LY Lam
# Please include the string: [perl #38434]
# in the subject line of all future correspondence about this issue.
# <URL: https://rt.perl.org/rt3/Ticket/Display.html?id=38434 >


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

Chromatic

unread,
Feb 5, 2006, 8:12:26 PM2/5/06
to perl5-...@perl.org, LY Lam, bugs-bi...@rt.perl.org
On Saturday 04 February 2006 22:43, LY Lam wrote:

> 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

Yitzchak Scott-Thoennes

unread,
Feb 5, 2006, 11:27:54 PM2/5/06
to chromatic, perl5-...@perl.org, LY Lam, bugs-bi...@rt.perl.org

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.

Chromatic

unread,
Feb 5, 2006, 11:49:18 PM2/5/06
to Yitzchak Scott-Thoennes, perl5-...@perl.org, LY Lam, bugs-bi...@rt.perl.org
On Sunday 05 February 2006 20:27, Yitzchak Scott-Thoennes wrote:

> 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

0 new messages