There's more than one way to do things PerlMonks

### Round a Number to Any Place

by reptile (Monk)
 on Aug 29, 2000 at 23:34 UTC Need Help??
 Description: I was having trouble sleeping one night, so I decided to bore myself to sleep by coming up with a way to round a number to any decimal place without using printf(). The solution I came up with also lets you round to any place at all (like 10s, 100s, etc.) To use this, call round() with the number you want to round, followed by a multiple of 10 describing to which place you want to round to (10 rounds to the nearest 10, .1 to the nearest 10ths, .001 to the nearest thousandth place... you get the idea). By the way, I benchmarked this and it is marginally faster than printf().
```sub round {
my (\$num, \$place) = @_;
my \$r = \$num / \$place;

return \$r - int(\$r) < .5 ?
int(\$r) * \$place :
(int(\$r) + 1) * \$place;
}
```
Replies are listed 'Best First'.
RE: Round a Number to Any Place
by Adam (Vicar) on Aug 30, 2000 at 04:45 UTC
As nuance pointed out above, your routine is not mathematically correct. Here is my attempt:
```sub round
{
my( \$num, \$place )= @_;
\$num = \$num/\$place;
my \$calc = int( \$num + 0.5 * ( \$num <=> 0 ) );
\$calc += (0 <=> \$num) if( \$calc % 2 and 0.5 == abs( int(\$num) - \$n
+um ) );
return \$calc * \$place
}
Oh, and don't bother debating the validity of rounding to the nearest even. I wasted several hours trying to do that the last time around.

NOTE: I think there is a bug in that code, but I'm tired and I couldn't place it.

In chatterbox, I (belatedly) noticed Adam wonder why his code failed for round(-4.35,0.1). I think the reason helps to illustrate why I didn't worry about rounding to the nearest even. I can sum up the situation pretty easily with this:

``` % perl -le "print 4.35*100-435"
-5.6843418860808e-014

A floating point number can never be exactly 0.05. Floating point numbers are represented in binary and have a limited number of bits. 0.05 in binary requires an infinite number of bits (forming a repeating pattern, similar to how 1/7 is 0.142857142857... in decimal). So the extra code to round to the nearest even will probably never trigger when rounding to 1 or more places after the decimal point (I say "probably" because I don't claim to be able to predict the interactions of limited floating point precision on the calculations being done and it is possible that things would cancel out and trigger the extra code, perhaps in cases where it really shouldn't have).

Okay, here is my version of Adam's code:

```sub round {
my( \$num, \$prec )= @_;
\$num= int( my \$frac= \$num/\$prec + 0.5 - (\$num<0) );
\$num -= (\$num<=>0)   if  \$num == \$frac  &&  \$num % 2;
return \$num*\$prec;
}
- tye (but my friends call me "Tye")
That makes sense, I don't know why I didn't think of that. Thanks!
RE: Round a Number to Any Place
by tye (Sage) on Aug 29, 2000 at 23:57 UTC

Nice work. Here are, I think, two improvements:

```sub round {
my( \$num, \$prec )= @_;
return  int( \$num/\$prec + 0.5 - (\$num<0) ) * \$prec;
}

This is slightly simpler and rounds negative numbers properly.

- tye (but my friends call me "Tye")
We've done this number rounding thing before. Have a look at the thread following this node to see why the 0.5 thing isn't quite exact.

Nuance

Create A New User
Node Status?
node history
Node Type: snippet [id://30195]
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (6)
As of 2018-06-22 00:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Should cpanminus be part of the standard Perl release?

Results (120 votes). Check out past polls.

Notices?