Well, that only rounds properly for positive numbers.
sub round {
my $n = shift;
int($n + .5 * ($n < 0 ? -1 : 1));
}
Then you might want to be able to round to a specific place:
sub round {
my ($n, $p) = @_;
$p ||= 0; # default to integer rounding
int($n * 10**$p + .5 * ($n < 0 ? -1 : 1)) / 10**$p;
}
Ta da. Now you can round 123.45678 to 123.457 by calling round(123.45678, 3), and you can round 123456789 to 123500000 by calling round(123456789, -5).
_____________________________________________________
Jeff[japhy]Pinyan:
Perl,
regex,
and perl
hacker.
s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??; | [reply] [d/l] [select] |
I like your round() function, though I'd have it take a "nearest x" parameter instead of a "number of digits" parameter. Just replace the ||=0 with ||=1 and 10**$p with $p. Then you can say round($inches, 1/8), or round($bagles, 12).
Thanks,
James Mastros,
Just Another Perl Scribe
| [reply] |
There is nothing wrong with your approach at all - Another method might be to employ the sprintf operator for which there is an excellent tutorial on this site. eg.
sub roundThatBadDog {
my $seconds = shift;
return sprintf("%d", $seconds);
}
Or alternatively, if you are wanting precision, replace the %d with %.xf where x is a integer value representing the order of decimal precision desired.
Update
In response to japhy's comments, I checked out the FAQ and ran a few timing tests to compare.
From my reading, the comments in the FAQ hardly suggest the avoidance of sprintf function for rounding purposes but rather the use of a more intrinsic, known method of rounding (such as those submitted by japhy here) for sensitive or high-value operations. The example given is a financial environment where the FAQ rightly suggests that a programmer should implement their own rounding code rather than relying on system functions.
With regard to a timing comparison, the following is the output - Make of it what you will :-)
Benchmark: timing 100000 iterations of japhy, rob_au...
japhy: 3 wallclock secs ( 4.13 usr + 0.00 sys = 4.13 CPU) @ 24
+213.08/s (n=100000)
rob_au: 4 wallclock secs ( 4.23 usr + 0.00 sys = 4.23 CPU) @ 23
+640.66/s (n=100000)
Update
These results may not be truly indicative of function performance, and may I add, not intendedly so. japhy discusses performance differences in his more comprehensive comparison here. ++japhy!
| [reply] [d/l] [select] |
#!/usr/bin/perl
use Benchmark 'cmpthese';
use strict;
my @n = (1.24, 5.43, -98.54, -73.667, 0.67, 2.34, 76.89, -999.99, 34.5
+2);
sub round {
my ($n, $p) = @_;
$p ||= 0;
int($n * 10**$p + .5 * ($n < 0 ? -1 : 1)) / 10**$p;
}
cmpthese(-5, {
rob_int => sub {
for (@n) { my $x = sprintf "%d", $_ }
},
rob_round => sub {
for (@n) { my $x = sprintf "%.0f", $_ }
},
japhy_int => sub {
for (@n) { my $x = int $_ }
},
japhy_round => sub {
for (@n) { my $x = round($_,0) }
},
japhy_inplace => sub {
for (@n) { my $x = int($_ + .5 * ($_ < 0 ? -1 : 1)) }
},
});
The results are thus:
Benchmark: running
japhy_int, japhy_round, japhy_inplace,
rob_int, rob_round
for at least 5 CPU seconds...
japhy_int: 20326.40/s (n=108543)
japhy_round: 3272.93/s (n= 17412)
japhy_inplace: 11470.71/s (n= 61483)
rob_int: 11797.18/s (n= 62643)
rob_round: 1964.03/s (n= 10429)
Rate rob_round japhy_round japhy_inplace rob_int japh
+y_int
rob_round 1964/s -- -40% -83% -83%
+ -90%
japhy_round 3273/s 67% -- -71% -72%
+ -84%
japhy_inplace 11471/s 484% 250% -- -3%
+ -44%
rob_int 11797/s 501% 260% 3% --
+ -42%
japhy_int 20326/s 935% 521% 77% 72%
+ --
What this all means is that, when it comes to truncating, it is about 75% faster to use int() and it is to use sprintf("%d"). It also shows that my function is 67% faster than using sprintf("%.0f") (which, I remind you, returns IEEE values which may not be appropriate for your computations). It also shows that if you were to inline my function call, you would run 250% faster -- sigh, the overhead of function calls.
This data also supports the previous finding, that my function, made in-place, runs about as fast as sprintf("%d").
_____________________________________________________
Jeff[japhy]Pinyan:
Perl,
regex,
and perl
hacker.
s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??; | [reply] [d/l] [select] |
| [reply] |
use POSIX;
sub round { floor($_[0] + 0.5) }
I've always wondered why Perl doesn't include a round function, it is something that tends to stump beginners.
I also wondered if this was faster than japhy's code.
Benchmark: timing 100000 iterations of round1, round2...
round1: 65 wallclock secs (64.59 usr + 0.00 sys = 64.59 CPU) @ 15
+48.23/s (n=100000)
round2: 47 wallclock secs (47.78 usr + 0.00 sys = 47.78 CPU) @ 20
+92.93/s (n=100000)
And it seems to be slightly faster. I think it would also be marginally more understandable from a maintanace standpoint.
Also remember that if you work with floating point numbers you might get things you don't expect, for example, adding 0.01 to 0 99 times gives you 0.990000000000001.
The module Math::FixedPrecision seems promising, but unfortunatly it rounds 0.50 to 0 rather than 1.
use Math::FixedPrecision;
for (my $i = Math::FixedPrecision->new(0); $i <= 1; $i += 0.01) {
print $i, "\t", Math::FixedPrecision->new($i + 0.01, 0), "\n";
}
I know I've gone off on a slight tangent here.. but is there some nicer way to deal with this. I haven't used Math::Currency but that does look promising.
gav^ | [reply] [d/l] [select] |
The module Math::FixedPrecision seems promising, but unfortunatly it rounds 0.50 to 0 rather than 1.
as far as i'm aware, rounding 'rules' are just conventions. they're only called rules because they provide a set of instructions.
the method i was taught was that in cases of .5 exactly, the number would round to the nearest even number. so, 0.5000000000001 rounds to 1, 0.5 rounds to 0.
~Particle
| [reply] |