nite_man has asked for the wisdom of the Perl Monks concerning the following question:
Dear monks!
I need to round a number up nearest ten, hundred, thousand etc i.e. 7 should be 10, 23 -> 30, 112 -> 120, 1325 -> 1400 ...
So, I've created function to do this convertion:
sub round_up {
my $num = shift || die "Please, specify target number!";
my $length = length($num);
my $first = substr($num, 0, 1);
if($length > 2) {
my $rest = substr($num, 1);
$num = $first.('0'x($length - 1)) +
((substr($rest, 0, 1) + 1).('0'x(length($rest) - 1
+)));
} elsif($length == 2) {
$num = (substr($num, 0, 1) + 1) . ('0'x($length-1));
} else {
$num = 10;
}
return $num;
}
This is the fist thing which has came to my mind. I'd like to see your variants of solving of this task. All suggestions will be appreciated.
Thanks in advanced.
---
Michael Stepanov aka nite_man
It's only my opinion and it doesn't have pretensions of absoluteness!
20040928 Edit by Steve_p: Changed title from 'Rounding up nearest ten, handred etc'
Re: Rounding up nearest ten, hundred etc
by sgifford (Prior) on Sep 27, 2004 at 16:26 UTC
|
sub roundoff
{
my $num = shift;
my $roundto = shift || 1;
return int($num/$roundto+0.5)*$roundto;
}
foreach my $i (0.6, 1.2, 38.4, 88.6, 92.5)
{
printf "%5.1f ",$i;
foreach my $j (1, 10, 100)
{
printf "%2d ", roundoff($i,$j);
}
print "\n";
}
Update: This actually rounds to the nearest number; I misread the OP's question. Here's a similar solution that always rounds up:
use POSIX qw(ceil);
sub roundup
{
my $num = shift;
my $roundto = shift || 1;
return int(ceil($num/$roundto))*$roundto;
}
Another Update: This function will find the right scale according to my understanding of the OPs question. You can use something like roundup($i,rightscale($j)) to get the auto-scaling behavior.
sub max
{
return $_[0] > $_[1] ? $_[0] : $_[1];
}
sub rightscale
{
my $num = shift;
return 10 ** max(int(log(abs($num))/log(10))-1,1);
}
| [reply] [d/l] [select] |
|
| [reply] |
Re: Rounding up nearest ten, hundred etc
by kvale (Monsignor) on Sep 27, 2004 at 16:44 UTC
|
Here is a solution that only involves simple arithmetic:
sub roundup
{
my $number = shift;
my $round = shift;
if ($number % $round) {
return (1 + int($number/$round)) * $round;
}
else {
return $number;
}
}
while (<DATA>) {
my ($n, $r) = split;
print "Nunber = $n Rounding = $r Roundup = ", roundup( $n, $r)
+, "\n";
}
__DATA__
7 10
23 10
112 10
1325 100
1500 100
| [reply] [d/l] |
Re: Rounding up nearest ten, hundred etc
by ikegami (Patriarch) on Sep 27, 2004 at 16:49 UTC
|
The OP's rounding is inconsistent. It rounds by 1) ten, 2) ten, 3) ten, 4) hundred for numbere of magnitude 1) unit, 2) ten, 3) hundred, 4) thousand. There's no pattern there.
So, I understood the question as "round up to one significant digit, be it 10s, 100s, 1000s, ... ". It even handles negative numbers, and numbers between +-1 and 0!
sub nearest {
local $_ = 0+$_[0];
return 0 unless $_;
my $f = $_ <=> 0;
$_ = abs($_);
while ($_ >= 10) { $_ /= 10; $f *= 10; }
while ($_ < 1) { $_ *= 10; $f /= 10; }
return int($_ + 0.5) * $f;
}
sub up_nearest {
local $_ = 0+$_[0];
return 0 unless $_;
my $f = $_ <=> 0;
$_ = abs($_);
while ($_ >= 10) { $_ /= 10; $f *= 10; }
while ($_ < 1) { $_ *= 10; $f /= 10; }
return (int($_) == $_ ? $_ : (int($_) + 1)) * $f;
}
foreach (
0,
5,
10,
50,
0.1,
5.1,
11,
51,
0.11,
5.5,
15,
55,
0.15,
5.9,
19,
59,
0.19,
) {
printf("%12.6f: %4s\n", $_, nearest( $_));
printf("%12.6f| %4s\n", $_, up_nearest( $_));
printf("%12.6f: %4s\n", -$_, nearest(-$_)) if $_;
printf("%12.6f| %4s\n", -$_, up_nearest(-$_)) if $_;
}
| [reply] [d/l] |
Re: Rounding up nearest ten, hundred etc
by Roy Johnson (Monsignor) on Sep 27, 2004 at 16:41 UTC
|
sub round_up {
my $n = shift;
my $scale = 10**int(log($n)/log(10));
$n = 9 if $scale == 1; #magic for single digits
if ($n > $scale) {
$n = int($n/$scale+1)*$scale;
}
$n;
}
Caution: Contents may have been coded under pressure.
| [reply] [d/l] |
Re: Rounding up nearest ten, hundred etc
by bobf (Monsignor) on Sep 27, 2004 at 16:53 UTC
|
As sgifford illustrated, you can use the int and sprintf functions to round to whole numbers. There is also the Math-Round module for rounding integers. The POSIX module has the ceil and floor functions, which you might want to tuck away for later.
BTW, sprintf can be used directly to round a number to a given number of decimal places. See perlfaq4, "Does Perl have a round() function?", for details.
HTH
Update: I see many other creative examples were posted in addition to sgifford's while I was distracted looking at Math-Round. If you want to roll your own, take your pick! :) The module looks interesting, though, specifically the nearest function.
| [reply] [d/l] [select] |
Re: Rounding up nearest ten, hundred etc
by terra incognita (Pilgrim) on Sep 27, 2004 at 18:48 UTC
|
Here is something using "mod". The function is pretty simple but the rounding requirements make it more complex.
Comments on where I can improve this code and what practices I should stay away from are appreciated.
use strict;
my $i;
my $roundup;
my $mod;
my $places;
my $modifier;
for $i (<DATA>) {
chomp $i;
$places = length $i;
$modifier = 10;
if ($places > 3) {
$modifier = 100;
}
$mod = $i % $modifier;
if ($mod) {
$roundup = $i + ($modifier - $mod);
print "$i rounded up is $roundup\n";
}else{
print "$i\n";
}
}
__DATA__
0
2
9
11
20
21
112
1000
1325
1453
1999
10000
10001
| [reply] [d/l] |
Re: Rounding up nearest ten, hundred etc
by traveler (Parson) on Sep 27, 2004 at 16:52 UTC
|
elsif($length == 2) {
$num = (substr($num, 0, 1) + 1) . ('0'x($length-1));
}
could be
elsif($length == 2) {
$num = (substr($num, 0, 1) + 1) . '0';
}
or even
elseif($length == 2){
$num = int($num/10) * 10;
}
since $length is known to be 2.
But I am confused. In your example numbers of 1, 2 and 3 digits round to the nearest 10 and numbers of 4 digits round to the nearest 100. Could you please be more explicit about when to round to what? | [reply] [d/l] [select] |
|
| [reply] |
|
What people are calling attention to is that your examples show us what you want for numbers 1-4 digits long, and the rule is different for numbers that are 1-3 digits long, and numbers that are 4 digits long. If we knew you were only ever interested in numbers 1-4 digits long, that'd be all we needed to know.
But if you're potentially interested in longer numbers, we're left with no idea what behavior you want or expect for numbers longer than 4 digits.
Basically, I don't know what to make of your 'etc.' above. Are numbers from 4-6 digits rounded to the next hundred, and then 7-9 digits rounded to the next thousand?
| [reply] |
Re: Rounding up nearest ten, hundred etc
by superfrink (Curate) on Sep 28, 2004 at 05:46 UTC
|
After reading the posts I realize the original post wasn't about just rounding up to the next multiple of ten but I thought it was interesting anyway so here is my answer. I thought it was neat but I had to fix it up to deal with negative numbers.
#!/usr/bin/perl -w
use strict;
sub round_up_tens($) {
my $n = int shift;
if(($n % 10) == 0) {
return($n);
} else {
my $sign = 1;
if($n < 0) { $sign = 0; }
$n = int ($n / 10);
$n *= 10;
if($sign) {
$n += 10;
}
return($n);
}
return(-1);
}
print "-24 => " . round_up_tens(-24) . "\n";
print "-14 => " . round_up_tens(-14) . "\n";
print "-10 => " . round_up_tens(-10) . "\n";
print "-4 => " . round_up_tens(-4) . "\n";
print "0 => " . round_up_tens(0) . "\n";
print "4 => " . round_up_tens(4) . "\n";
print "7 => " . round_up_tens(7) . "\n";
print "10 => " . round_up_tens(10) . "\n";
print "14 => " . round_up_tens(14) . "\n";
print "144 => " . round_up_tens(144) . "\n";
Prints:
-24 => -20
-14 => -10
-10 => -10
-4 => 0
0 => 0
4 => 10
7 => 10
10 => 10
14 => 20
144 => 150
| [reply] [d/l] [select] |
|
|