It's not an anomaly.
Perl rounds scientifically. This is not the same way of rounding as accountants use. Accountants always round .5 up. That's probably what you were taught at school. It's predictable and good.
Scientists don't always round .5 up. .5 is exactly half way between two numbers. If they always rounded up, you would get statistical bias in large collections of rounded data. .5 is rounded sometimes up and sometimes down. (I think the rule is something like even numbers (2.5, 4.5, 6.5 etc.) round down while odd numbers (1.5, 3.5 etc.) round up.) This method is scientifically correct, and good.
Of course, there are also problems with INTs and FLOATs. For accounting correctness, one thing you can do is cheat:
# untested
if ($unrounded =~ /\.(\d)/ and $1 > 4) {$rounded = int ($unrounded + 1
+)}
else {$rounded = int $unrounded}
dave hj~ | [reply] [d/l] |
In base 10, or any base equal to 2 modulo 4, round to even is preferred.
In a base equal to 0 modulo 4, round to odd would be preferred.
| [reply] |
What HW/OS/Perl version are you on ?
On my Activestate 631 both variations in your example is rounded up.
What I'm thinking of is that a floating point value not always kan be represented exactly and that your first and second case might be represented as 4.499999999999999..... in your box, which would explain the rounddown.
You could look into the Posix routines for a possibly alternate route to achieve your aim.
--- I would like to change the world but God won't let me have the source code.
| [reply] |
Intel chip, Linux (cobalt's hacked Redhat version), and Perl 5.6.1. POSIX 'floor' and 'ceiling' are mentioned in a couple of posts elsewhere on perlmonks. I'll look into those.
I think the suggestion that the 5 isn't really a '5' holds the most water. There are actually about 28 separate calculations being performed in that section and it is only in THAT PARTICULAR section that the round doesn't perform as expected.
Okay, I'm happy again. I just wanted an explanation for the (apparently) freaky behaviour.
-oakbox
| [reply] |
I think if you want a proper rounding, you do this:
$x=int($num+0.5);
This gets rid of all those oddities, like a number looking like 5.5, but actually being 4.9999999....
Sorry, typo, should be 5.499999...
| [reply] [d/l] |
I dunno what to tell you, except you're dealing with floats... deal with ints instead ... (I have no insight, only code, well a little insight - perl has internal ways of distinguishing numbers from strings .... (s)printf are c functions, do stuff similar to (un)pack in fsking with the numbers ... that kinda it, vague ain't it )
#!/usr/bin/perl
my $one = 7 + (2 - (0.5 * 9));
my $two = 7 + (2 - (0.5 * 9));
my $thr = 7 + (5 - (0.5 * 9));
for($one,$two,$thr) {
printf "no_ROUND(%s) yes_ROUND(%d)
foy_FLOAT(%f) foy_ROUND(%.0f)\n\n",
$_,$_,$_,$_;
=head2 UPDATE: uncomment this to add to the mistery
print "pack i ".unpack('i*', pack('i*', $_))."\n";
print "pack l ".unpack('l*', pack('l*', $_))."\n";
print "pack f ".unpack('f*', pack('f*', $_))."\n";
print "pack d ".unpack('d*', pack('d*', $_))."\n";
=cut and don't forget cut#
}
__END__
=pod
C:\>perl round.txt
no_ROUND(4.5) yes_ROUND(4)
foy_FLOAT(4.500000) foy_ROUND(5)
no_ROUND(4.5) yes_ROUND(4)
foy_FLOAT(4.500000) foy_ROUND(5)
no_ROUND(7.5) yes_ROUND(7)
foy_FLOAT(7.500000) foy_ROUND(8)
=head1 that is an interesting way to get an int
%% a percent sign
%c a character with the given number
%s a string
%d a signed integer, in decimal
%u an unsigned integer, in decimal
%o an unsigned integer, in octal
%x an unsigned integer, in hexadecimal
%e a floating-point number, in scientific notation
%f a floating-point number, in fixed decimal notation
%g a floating-point number, in %e or %f notation
=cut
update: guha brings up a good point, ints/floats will still vary from system 2 system
| [reply] [d/l] |
the latest math::round's nearest function does check for big-endian or little-endian then does the right thing. Otherwise, a fix in another module uses 0.500000001 to do rounding. Of course this also exists in javascript where the simple solution is to use 0.500000001. | [reply] |