I suspected that this was the intentional behavior of the function.  For
what I am trying to do, I don't think "banker's rounding" will work.  Not
being a "real" programmer, perhaps I am approaching my problem from the
wrong angle.

I am writing a script that works with a piece of CAM software to create a
drawn pattern.  The total number of patterns I need will vary each time the
program is run, but the number will always be an integer.  The trouble is
that I have to alternate the patterns from the left side of the design to
the right side of the design.  When I have an even number of patterns, this
isn't a problem.  $ patterns means two patterns on the left and two on the
right.  The rule I must follow for odd numbers is to divide by two, round up
to the next highest number, and put the higher number of patterns on the
left.  So for 5 patterns, I would have 3 on the left and two on the right.
What I am trying to do is to get the number of patterns, divide by two,
round up, then subtract this value from the total to obtain the right and
left numbers.  So in my case I always want to round up.  Unless there is a
better way to approach a problem like this....

Thanks for the help and the info.  It helps a novice programmer such as
myself to understand things a bit better.




.-.  --..

 -----Original Message-----
From:   zsdc [mailto:[EMAIL PROTECTED] 
Sent:   Tuesday, February 24, 2004 2:29 PM
To:     Zielfelder, Robert
Cc:     Perl Beginners List (E-mail)
Subject:        Re: Rounding of floating point numbers

Zielfelder, Robert wrote:

> One other odd thing I noticed using the sprintf method: 
> If $var is odd, $var2 rounds low.  If $var is even, then $var2 rounds
high.
> Being a programmer by necessity rather than by choice forces me to take
the
> easy way out and use the POSIX solution to get the program written.
> Although, it would be interesting to know why the sprintf method behaves
the
> way it does...

Didn't you mean the other way around, i.e. rounding to the nearest even 
integer, instead of odd as you wrote above?

It's a banker rounding, an old way of rounding numbers used even in 
times when bankers were doing it manually. It's a way to ensure that 
your results are not skewed in any particular direction, to minimize the 
accumulation of rounding errors after adding numbers together.

When the fraction part is exactly 0.5, i.e. it's equally far from both 
integers it is between, then it rounds to the even one. Why even and not 
odd? Only because it's easier to divide by 2 without introducing more 
errors.

Run this program:

#!/usr/bin/perl -w

for $f (.49, .50, .51) {
     for $i (0..5) {
         printf "%.2f -> %.0f\n", $i + $f, $i + $f;
     }
}
__END__

It will print:

0.49 -> 0
1.49 -> 1
2.49 -> 2
3.49 -> 3
4.49 -> 4
5.49 -> 5
0.50 -> 0
1.50 -> 2
2.50 -> 2
3.50 -> 4
4.50 -> 4
5.50 -> 6
0.51 -> 1
1.51 -> 2
2.51 -> 3
3.51 -> 4
4.51 -> 5
5.51 -> 6

Numbers with .49 are always rounded down (like with floor) to get the 
nearest integer, numbers with .51 are always rounded up (like with ceil) 
-- still, no problem with finding the nearest integer -- but numbers 
with .50 not having a nearest integer, are rounded to the nerest even 
integer.

This not Perl-specific at all. This C program:

#include <stdio.h>

int main() {
     float i, f;
     for (f = 0.49; f <= 0.51; f += 0.01)
         for (i = 0; i <= 5; i++)
             printf("%.2f -> %.0f\n", i + f, i + f);
     return 0;
}
/* END */

prints exactly the same output as the above Perl program.

I haven't read everything in this thread so I don't know what results 
are you exactly expecting, but I think that you probably should use 
Math:: modules, like Charles suggested.

Do you want numbers with decimal part .5 to always round up? Keep in 
mind that it will skew your results. See this program:

#!/usr/bin/perl -w

use POSIX qw(floor ceil);

sub round1 { sprintf '%.0f', @_ }
sub round2 {
     $x = shift;
     $r = floor($x);
     if ($x - $r >= 0.5) { $r = ceil($x) }
     return $r;
}

for (1..10000) {
     $x = 0.1 * int rand 100;
     $sum += $x;
     $rsum1 += round1($x);
     $rsum2 += round2($x);
}

printf "Real:\t%.1f\n", $sum;
print "Banker:\t$rsum1\n";
print "5up:\t$rsum2\n";

__END__

The function round1() rounds just like printf and the function round2() 
rounds .5 always up. The program adds random numbers between 0.0 and 9.9 
and prints the real sum and the sums of numbers rounded with both 
methods. See that the second one is always considerably larger. Every 
rounded number is skewed up by 5% on average. This is exactly why we use 
banker rounding.

-- 
ZSDC

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
<http://learn.perl.org/> <http://learn.perl.org/first-response>


Reply via email to