I was looking for a ceil(x,y) function similar to what exists in excel, where x is the thing to round, and y is "significance" according to Excel v9 (Office 2000), I prefer to think of it as "interval".
But, I was also intrigued by several of the other answers given (found via supersearch), and then in a fit of playing around, I created several variations below.
My personal restrictions were to use math operations, and not rely on other modules. This eliminated the printf and POSIX answers.
use strict;
use warnings;
########
# This version takes two arguments
# The number to round
# And the number of places to the right or left of the decimal poin
+t
# Positive numbers to the left, negative numbers to the right.
# Think powers of 10.
#
# Parts of this were stolen from nodeid=8781, and nodeid=1873
# most notably from Roy Johnson and wrvhage
########
sub round {
my ($number, $places) = @_;
my $sign = ($number < 0) ? '-' : '';
my $abs = abs($number);
if($places < 0) {
$places *= -1;
return $sign . substr($abs+("0." . "0" x $places . "5"),
0, $places+length(int($abs))+1);
} else {
my $p10 = 10**$places;
return $sign . int($abs/$p10 + 0.5)*$p10;
}
}
########
# Simple Ceiling function
########
sub ceil {
my ($num) = @_;
return int($num) + ($num > int($num));
}
########
# Function modeled after Excel's two argument function
# Number to act on
# Interval to return (2 would return only multiples of 2, 3 multipl
+es of 3 etc)
########
sub ceil_xl {
my ($num,$interval) = @_;
return ceil($num / $interval) * $interval;
}
########
# Function derived from node_id=270920
# Returns next nearest mulitple of 5 up to 50, then nearest 25 up t
+o 100,
# then nearest quarter of current power of 10.
########
sub ceil_qtrs {
my ($num) = @_;
my $abs=int(abs($num));
my $interval;
# This next line was the originally given answer from Abigail-II,
# it was obtuse enough that I needed to break it down to fully unders
+tand it,
# then I wanted to modify it, and I then left my version in the more
+readable
# style.
# my $frac = $num < 100 ? 5 : (1 . ("0" x (length ($num) - 1))) /
+ 4;
if($abs < 40) {
$interval = 5;
} elsif($abs < 100) {
$interval = 25;
} else {
$interval = "1".("0"x(length($abs)-1));
$interval = $interval/4;
}
return ceil($num / $interval) * $interval;
}
my @data = qw(1 2 3.14159 4.634 5 5.165 6 9 10 10.257 13 23 89 99 100
+101 214 702 1328
-1 -2 -3.14159 -4.634 -5 -5.165 -6 -9 -10 -10.257 -13 -23 -89 -99 -
+100 -101 -214 -704 -1328
);
my $format = " "."%13.13s "x8 . "\n";
printf ($format, "number","ceil_qtrs","ceil_xl(x,3)","ceil_xl(x,8)","c
+eil(x)","round(x,0)","round(x,1)","round(x,-2)");
foreach (@data) {
printf ("%13.13s ",$_);
printf ("%13d ",ceil_qtrs($_));
printf ("%13d ",ceil_xl($_,3));
printf ("%13d ",ceil_xl($_,8));
printf ("%13d ",ceil($_));
printf ("%13d ",round($_,0));
printf ("%13d ",round($_,1));
printf ("%13.3f ",round($_,-2));
print "\n";
}