I hate decimal numbers. For 1.005 I don't get the result I expect with the following code.
#!/usr/bin/perl -w
use strict;
use POSIX qw(floor);
my $num = (1.005 * 100) + 0.5;
print $num . "\n"; # 101
print floor($num) . "\n"; # 100
print int($num) . "\n"; # 100
For 2.005 and 3.005 it works fine.
With this ugly "hack" I get the expected result.
#!/usr/bin/perl -w
use strict;
use POSIX qw(floor);
my $num = (1.005 * 100) + 0.5;
$num = "$num";
print $num . "\n"; # 101
print floor($num) . "\n"; # 101
print int($num) . "\n"; # 101
What is the correct way to do this?
floor()
is not for rounding, it goes down to the nearest integer.
See this old post: How do you round a floating point number in Perl?
The “correct” way to round is whatever way you choose to define as correct for whatever purpose you have in mind. People have thought a lot about it, and some strategies are more appropriate for some application areas than they are in others.
Rounding toward zero by using int()
is seldom what people want. Usually they want something that is numerically unbiased.
Fair splits the fives; hence the shorthand term, “round towards even”. The nearer integer isn’t defined: there is no nearer integer when the least siginficant digit is 5. There are 9 things that when rounded give a different answer: 1,2,3,4,5,6,7,8,9.
To be fair, you must have half of those go one way and half go the other. But there are nine numbers, so you have four go one way and four go the other, but now you have a fairness problem. The only way therefore to avoid bias is for the 5 cases to alternate up and down. That’s why it works this way.
% perl -e 'printf "%.0f\n", $_+.5 for -10 .. +10'
produces the sequence
-10 -8 -8 -6 -6 -4 -4 -2 -2 -0 0 2 2 4 4 6 6 8 8 10 10
While this round-towards-zero approach:
% perl -e 'print int($_+.5)," " for -10 .. +10; print "\n"'
makes this with a hump in the middle:
-9 -8 -7 -6 -5 -4 -3 -2 -1 0 0 1 2 3 4 5 6 7 8 9 10
Looking again at round-towards-even:
% perl -le 'printf "%.1f ", $_+.05 for -10 .. +10'
-9.9 -8.9 -8.0 -7.0 -6.0 -5.0 -4.0 -3.0 -1.9 -0.9 0.1 1.1 2.0 3.0 4.0 5.0 6.0 7.0 8.1 9.1 10.1
That makes more sense if you do this:
% perl -le 'printf "%.2f ", $_+.05 for -10 .. +10'
-9.95 -8.95 -7.95 -6.95 -5.95 -4.95 -3.95 -2.95 -1.95 -0.95 0.05 1.05 2.05 3.05 4.05 5.05 6.05 7.05 8.05 9.05 10.05
and then consider what towards even means.