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

Reply via email to