-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Actually, Bill's answer has an error.. Adding .05 just introduces a subtle
error because some
decimal floating point numbers cannot be exactly represented in binary form.
For example:
my ($i, $j);
foreach $i (0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5) {
$j = $i + .05;
printf "%.20f\t%.20f\t%.1f\n", $i, $j, $j;
}
Produces:
0.50000000000000000000 0.55000000000000004000 0.6
1.50000000000000000000 1.55000000000000000000 1.6
2.50000000000000000000 2.54999999999999980000 2.5
3.50000000000000000000 3.54999999999999980000 3.5
4.50000000000000000000 4.54999999999999980000 4.5
5.50000000000000000000 5.54999999999999980000 5.5
6.50000000000000000000 6.54999999999999980000 6.5
7.50000000000000000000 7.54999999999999980000 7.5
8.50000000000000000000 8.55000000000000070000 8.6
9.50000000000000000000 9.55000000000000070000 9.6
See how looking 20 digits into a floating point value shows that it's not
exact, and then when
printf tries to print only one decimal point, it sometimes rounds down to .5
and sometimes
up to .6? It's not that the computer cannot do math.. That's what they do best.
It's that while
we "think" of the numbers as 0.55, 1.55, 2.55, etc, the computer sees them very
differently.
I think what Bill really meant to do.. To round a whole number (an integer),
use something
like this:
my ($i, $j);
foreach $i (0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5) {
$j = int($i + .5);
printf "%.20f\t%.20f\t%.0f\n", $i, $j, $j;
}
Which gives:
0.50000000000000000000 1.00000000000000000000 1.0
1.50000000000000000000 2.00000000000000000000 2.0
2.50000000000000000000 3.00000000000000000000 3.0
3.50000000000000000000 4.00000000000000000000 4.0
4.50000000000000000000 5.00000000000000000000 5.0
5.50000000000000000000 6.00000000000000000000 6.0
6.50000000000000000000 7.00000000000000000000 7.0
7.50000000000000000000 8.00000000000000000000 8.0
8.50000000000000000000 9.00000000000000000000 9.0
9.50000000000000000000 10.00000000000000000000 10.0
In other words, don't rely on [s]printf to do your rounding for you..
Especially if you're going
to add .5 or .05, etc, to do your own rounding on top of [s]printf's. You won't
get proper
results. You only add .5 if you're doing an integer truncation to *simulate*
rounding to an
integer value. To round to a floating point position, just scale the number
before adding .5
and using int() to trunc. For example, to round 4.567 to the nearest 0.01 (one
hundredth), do
this:
my $i = 4.567;
printf "%.20f\t", $i;
$i *= 100;
$i = int($i + .5);
$i /= 100;
printf "%.20f\n", $i;
Which gives:
4.56700000000000020000 4.57000000000000030000
Notice there's still that 2 (or 3) wayyyyy out there at the 16th digit? That's
the guard digit,
since floating point (C's type double on my machine) only guarantees 15 digits
of decimal
accuracy. The 16th digit is there to protect and round the result to 15 digits.
For all intents
and purposes, it's simply 4.57 -- but computers are are well-known for being
very literal
when it comes to numbers.. IE, those guard digits sometimes cause the very
problem
they're supposed to prevent! ;-) The error is compounded the more operations
you perform
on a number.
Incidentally, perl is a typeless language, compounding the problem even more.
That is, perl
converts a variable or literal to an integer, a floating point double, or text
string on-the-fly as
needed. A single line of perl code may hide several such conversions within the
perl core,
with each operation adding more onto the error until it becomes noticeable.
HTH,
Cas
On 16 Mar 2007 at 22:21, Su, Yu (Eugene) wrote:
> Interesting. Any explanation why sprintf treats 3.5 and 4.5
> differently in rounding?
>
> Thanks Bill.
>
> -Eugene
>
>
> -----Original Message-----
> From: Bill Luebkert [mailto:[EMAIL PROTECTED]
> Sent: Friday, March 16, 2007 5:33 PM
> To: Su, Yu (Eugene)
> Cc: [email protected]
> Subject: Re: how to round a floating-point value to an integer value
>
>
> Su, Yu (Eugene) wrote:
> > I expect
> > print sprintf("%.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f",
> > 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5); will give me 1 2
> > 3 4 5 6 7 8 9 10 but instead I get 0 2 2 4 4 6 6 8 8 10
> >
> > How to round a floating-point value to an integer value using (.5)
> > up rule?
>
> I'd add .5 in the next place down :
>
> foreach (0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0) {
> printf "%.1f => %.0f\n", $_, $_ + .05;
> }
>
> 0.0 => 0
> 0.1 => 0
> 0.2 => 0
> 0.3 => 0
> 0.4 => 0
> 0.5 => 0
> 0.6 => 1
> 0.7 => 1
> 0.8 => 1
> 0.9 => 1
> 1.0 => 1
>
> _______________________________________________
> Perl-Win32-Users mailing list
> [email protected]
> To unsubscribe: http://listserv.ActiveState.com/mailman/mysubs
>
>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (MingW32)
iD8DBQFF+3MJtSHnwqiHnD4RArZzAJ9tse3TEsISGOz2uiqN2jPMEn+mPACdGysA
TvC6I6zHpDSvumFZFnaTqhw=
=jwmh
-----END PGP SIGNATURE-----
_______________________________________________
Perl-Win32-Users mailing list
[email protected]
To unsubscribe: http://listserv.ActiveState.com/mailman/mysubs