Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Rounding up nearest ten, hundred etc

by nite_man (Deacon)
on Sep 27, 2004 at 16:15 UTC ( [id://394224]=perlquestion: print w/replies, xml ) Need Help??

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'

Replies are listed 'Best First'.
Re: Rounding up nearest ten, hundred etc
by sgifford (Prior) on Sep 27, 2004 at 16:26 UTC

    Here's a straightforward solution based on something I used to do on my Commodore 64. :)

    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); }

      Thanks for your function, sgifford! It works perfect and it's shorter and more clear!

      ---
      Michael Stepanov aka nite_man

      It's only my opinion and it doesn't have pretensions of absoluteness!

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

    -Mark

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 $_; }
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.
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.

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
Re: Rounding up nearest ten, hundred etc
by traveler (Parson) on Sep 27, 2004 at 16:52 UTC
    First, this
    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?

      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?

      It need to be rounded like that to find maximum number which will be used to build Y scale on the bar chart. That's way I need to round small numbers up to nearest ten, but big numbers - up to handred etc.

      ---
      Michael Stepanov aka nite_man

      It's only my opinion and it doesn't have pretensions of absoluteness!

        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?

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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://394224]
Approved by Arunbear
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-04-19 05:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found