# Rounding a float in Perl?

Discussion in 'Perl Misc' started by jon rogers, Oct 27, 2003.

1. ### jon rogersGuest

Hi

Is there any good way to round a float into n decimals in Perl?

I'd like to see

round(\$float,5); # rounds \$float to (at most) 5 decimal digits

which would turn
1.234446732653623
into
1.23445

(or some equivalent functionality)?

JR

jon rogers, Oct 27, 2003

2. ### Anno SiegelGuest

Bernard El-Hagin <> wrote in comp.lang.perl.misc:
> jon rogers <> wrote in news:bnimib\$38\$:
>
> > Hi
> >
> > Is there any good way to round a float into n decimals in Perl?
> >
> > I'd like to see
> >
> > round(\$float,5); # rounds \$float to (at most) 5 decimal digits

>
> [...]
>
>
> perldoc -q round

The FAQ answer is "use sprintf()", which is fine in most cases. It
must be said, however, that sprintf() is a slow function, and if a
lot of rounding is going on it can easily dominate the calculation.
Even a pure Perl rounding function, along the lines of

sub round {
my \$x = shift;
my \$y = 0.5 + abs \$x;
my \$abs = int \$y;
\$abs -= \$abs % 2 if \$y == \$abs;
(\$x <=> 0) * \$abs;
}

is twice as fast, and a compiled rounding function can be ten times
as fast.

Anno

Anno Siegel, Oct 27, 2003

3. ### Bart LateurGuest

jon rogers wrote:

>Is there any good way to round a float into n decimals in Perl?
>
>I'd like to see
>
>round(\$float,5); # rounds \$float to (at most) 5 decimal digits
>
>which would turn
>1.234446732653623
>into
>1.23445

sprintf

\$float = 1.234446732653623;
\$rounded = sprintf "%.5f", \$float;
print \$rounded;

However, that doesn't remove unnecessary trailing zeroes, or the decimal
point for integers.

--
Bart.

Bart Lateur, Oct 27, 2003
4. ### Philip NewtonGuest

On 27 Oct 2003 11:27:56 GMT, -berlin.de (Anno
Siegel) wrote:

> The FAQ answer is "use sprintf()", which is fine in most cases. It
> must be said, however, that sprintf() is a slow function, and if a
> lot of rounding is going on it can easily dominate the calculation.
> Even a pure Perl rounding function, along the lines of
>
> sub round {
> my \$x = shift;
> my \$y = 0.5 + abs \$x;
> my \$abs = int \$y;
> \$abs -= \$abs % 2 if \$y == \$abs;
> (\$x <=> 0) * \$abs;
> }
>
> is twice as fast, and a compiled rounding function can be ten times
> as fast.

But that doesn't allow you to specify the number of decimal places to
round to, does it?

And I wonder whether, once you add the scaling necessary to support
that, it's still faster than sprintf.

Cheers,
Philip
--
Philip Newton <>
That really is my address; no need to remove anything to reply.
If you're not part of the solution, you're part of the precipitate.

Philip Newton, Oct 29, 2003
5. ### Anno SiegelGuest

Philip Newton <> wrote in comp.lang.perl.misc:
> On 27 Oct 2003 11:27:56 GMT, -berlin.de (Anno
> Siegel) wrote:
>
> > The FAQ answer is "use sprintf()", which is fine in most cases. It
> > must be said, however, that sprintf() is a slow function, and if a
> > lot of rounding is going on it can easily dominate the calculation.
> > Even a pure Perl rounding function, along the lines of
> >
> > sub round {
> > my \$x = shift;
> > my \$y = 0.5 + abs \$x;
> > my \$abs = int \$y;
> > \$abs -= \$abs % 2 if \$y == \$abs;
> > (\$x <=> 0) * \$abs;
> > }
> >
> > is twice as fast, and a compiled rounding function can be ten times
> > as fast.

>
> But that doesn't allow you to specify the number of decimal places to
> round to, does it?

No, it doesn't. I don't remember the last time I wanted to round to
anything but the nearest integer, but the objection is valid.

> And I wonder whether, once you add the scaling necessary to support
> that, it's still faster than sprintf.

It (i.e. my implementation on my machine) is still 44% faster than sprintf,
as opposed to 113% for the non-scaling version. Then again, it can also
round 1234 to 1000 for a negative "number of decimal places", something
sprintf doesn't do.

But we're approaching bean-counting territory here...

Anno

Anno Siegel, Oct 29, 2003
6. ### Bart LateurGuest

Anno Siegel wrote:

>> But that doesn't allow you to specify the number of decimal places to
>> round to, does it?

>
>No, it doesn't. I don't remember the last time I wanted to round to
>anything but the nearest integer, but the objection is valid.

Think "currency".

--
Bart.

Bart Lateur, Oct 29, 2003
7. ### Anno SiegelGuest

Bart Lateur <> wrote in comp.lang.perl.misc:
> Anno Siegel wrote:
>
> >> But that doesn't allow you to specify the number of decimal places to
> >> round to, does it?

> >
> >No, it doesn't. I don't remember the last time I wanted to round to
> >anything but the nearest integer, but the objection is valid.

>
> Think "currency".

Oh, sure, it happens. Though, particularly with financial calculations,
an accepted technique is to first convert everything to cents (or whatever).

Anno

Anno Siegel, Oct 29, 2003
8. ### Jürgen ExnerGuest

Bart Lateur wrote:
> Anno Siegel wrote:
>
>>> But that doesn't allow you to specify the number of decimal places
>>> to round to, does it?

>>
>> No, it doesn't. I don't remember the last time I wanted to round to
>> anything but the nearest integer, but the objection is valid.

>
> Think "currency".

But you don't use floats for currency calculations.

jue

Jürgen Exner, Oct 29, 2003
9. ### Ilya ZakharevichGuest

[A complimentary Cc of this posting was sent to
Anno Siegel
<-berlin.de>], who wrote in article <bno3ra\$1p7\$-Berlin.DE>:
> It (i.e. my implementation on my machine) is still 44% faster than sprintf,
> as opposed to 113% for the non-scaling version. Then again, it can also
> round 1234 to 1000 for a negative "number of decimal places", something
> sprintf doesn't do.
>
> But we're approaching bean-counting territory here...

On my machine (EMX on 850MHz Athlon) your version takes 5.03 us per
iteration (when non-scaling). sprintf takes 0.15 us per iteration.
Apparently your CRT implementation is completely broken speedwise...
Enough said.

Hope this helps,
Ilya

Ilya Zakharevich, Oct 29, 2003
10. ### Anno SiegelGuest

Ilya Zakharevich <> wrote in comp.lang.perl.misc:
> [A complimentary Cc of this posting was sent to
> Anno Siegel
> <-berlin.de>], who wrote in article
> <bno3ra\$1p7\$-Berlin.DE>:
> > It (i.e. my implementation on my machine) is still 44% faster than sprintf,
> > as opposed to 113% for the non-scaling version. Then again, it can also
> > round 1234 to 1000 for a negative "number of decimal places", something
> > sprintf doesn't do.
> >
> > But we're approaching bean-counting territory here...

>
> On my machine (EMX on 850MHz Athlon) your version takes 5.03 us per
> iteration (when non-scaling). sprintf takes 0.15 us per iteration.
> Apparently your CRT implementation is completely broken speedwise...
> Enough said.

If so, that appears to be the case on more than one machine. I'm
getting consistent results (i.e. Perl rounding beats sprintf rounding)
on several machines. I'm appending the benchmarks i used for reference.

Anno

#!/usr/local/bin/perl
use strict; use warnings; \$| = 1;
use Benchmark qw( :all);

goto bench;

for ( -10 .. 10 ) {
my \$x = \$_ * 0.1;
my \$sp = sround( \$x);
my \$nin = nround( \$x);
my \$iin = cround( \$x);
print "\$x -> \$sp, \$nin, \$iin\n";
}
exit;

bench:

cmpthese( -5, {
sround => 'sround( rand( 100))',
nround => 'nround( rand( 100))',
iround => 'iround( rand( 100))',
});

###################################################################

# Perl, scaling
sub nround {
my ( \$x, \$n) = @_;
my \$pow10 = 10**(\$n || 0);
\$x *= \$pow10;
my \$y = 0.5 + abs \$x;
my \$abs = int \$y;
\$abs -= \$abs % 2 if \$y == \$abs;
( \$x <=> 0) * \$abs/\$pow10;
}

# Perl, non-scaling
sub iround {
my \$x = shift;
my \$y = 0.5 + abs \$x;
my \$abs = int \$y;
\$abs -= \$abs % 2 if \$y == \$abs;
( \$x <=> 0) * \$abs;
}

# sprintf
sub sround {
my \$x = shift;
0 + sprintf '%.0f', \$x;
}

Anno Siegel, Oct 31, 2003
11. ### Ilya ZakharevichGuest

[A complimentary Cc of this posting was sent to
Anno Siegel
<-berlin.de>], who wrote in article <bntfci\$gjv\$-Berlin.DE>:
> > On my machine (EMX on 850MHz Athlon) your version takes 5.03 us per
> > iteration (when non-scaling). sprintf takes 0.15 us per iteration.
> > Apparently your CRT implementation is completely broken speedwise...
> > Enough said.

> If so, that appears to be the case on more than one machine. I'm
> getting consistent results (i.e. Perl rounding beats sprintf rounding)
> on several machines. I'm appending the benchmarks i used for reference.

get results similar to what you describe.

Which means: it makes sense to special-case several formats before
they are handled to

perl -V:d_Gconvert

Any takers?

Ilya

Ilya Zakharevich, Nov 1, 2003
12. ### Ilya ZakharevichGuest

[A complimentary Cc of this posting was sent to
Anno Siegel
<-berlin.de>], who wrote in article <bnivfs\$i76\$-Berlin.DE>:
> The FAQ answer is "use sprintf()", which is fine in most cases. It
> must be said, however, that sprintf() is a slow function, and if a
> lot of rounding is going on it can easily dominate the calculation.
> Even a pure Perl rounding function, along the lines of
>
> sub round {
> my \$x = shift;
> my \$y = 0.5 + abs \$x;
> my \$abs = int \$y;
> \$abs -= \$abs % 2 if \$y == \$abs;
> (\$x <=> 0) * \$abs;
> }

Today I sent a patch which sped up sprint "%.0f" 15x.

Hope this slightly compensates my goof with having a constant-folded
benchmark ;-),
Ilya

Ilya Zakharevich, Nov 4, 2003
13. ### Anno SiegelGuest

Ilya Zakharevich <> wrote in comp.lang.perl.misc:
> [A complimentary Cc of this posting was sent to
> Anno Siegel
> <-berlin.de>], who wrote in article
> <bnivfs\$i76\$-Berlin.DE>:
> > The FAQ answer is "use sprintf()", which is fine in most cases. It
> > must be said, however, that sprintf() is a slow function, and if a
> > lot of rounding is going on it can easily dominate the calculation.
> > Even a pure Perl rounding function, along the lines of
> >
> > sub round {
> > my \$x = shift;
> > my \$y = 0.5 + abs \$x;
> > my \$abs = int \$y;
> > \$abs -= \$abs % 2 if \$y == \$abs;
> > (\$x <=> 0) * \$abs;
> > }

>
> Today I sent a patch which sped up sprint "%.0f" 15x.

Ah... that's about as fast as it gets. Now we can promote the FAQ answer
to rounding in good conscience.

> Hope this slightly compensates my goof with having a constant-folded
> benchmark ;-),

Well, it happens. When Perl 5 was new, I presented a benchmark to p5p,
proving that an extensive patch to the % operator didn't cost any time
at all. Both sides of the benchmark where constant-folded. Larry had
to set me straight.

Anno

Anno Siegel, Nov 4, 2003
14. ### Roy JohnsonGuest

-berlin.de (Anno Siegel) wrote in message news:<bntfci\$gjv\$-Berlin.DE>...
Your rounding is faster, but it doesn't quite work, in the sense of
always getting the same result as sprintf *or* the mathematically
accurate result.

nround(0.05, 1) yields 0, whereas sprintf('%.1f', 0.05) yields 0.1. Of
course, sprintf('%.1f', 3.05) yields 3.0, and so does Nround.

Here's one that works (in the sense of being mathematically accurate)
for all the values I've tried, but it is only about half as fast as
nround, or 3/4 as fast as sprintf.

sub stround {
my (\$n, \$places) = (@_,0);
my \$sign = (\$n < 0) ? '-' : '';
my \$abs = abs(\$n);
\$sign . substr(\$abs+('0.'.'0'x\$places.'5'),
0, \$places+length(int(\$abs))+1);
}

Roy Johnson, Nov 4, 2003
15. ### Anno SiegelGuest

Roy Johnson <> wrote in comp.lang.perl.misc:
> -berlin.de (Anno Siegel) wrote in message
> news:<bntfci\$gjv\$-Berlin.DE>...
> Your rounding is faster, but it doesn't quite work, in the sense of
> always getting the same result as sprintf *or* the mathematically
> accurate result.

What is the mathematically accurate result of rounding 0.05 to one
decimal place? The problem is, there are two equally likely candidates,
and mathematics doesn't presume to make a decision. Mathematically,
the result isn't defined.

> nround(0.05, 1) yields 0, whereas sprintf('%.1f', 0.05) yields 0.1. Of
> course, sprintf('%.1f', 3.05) yields 3.0, and so does Nround.

Algorithms have to make a decision, and standards define how to make
it. I am a little surprised at what IEEE does here (assuming it *is*
IEEE), or rather, I would be surprised if I hadn't long ago given up
being surprised by numeric results.

> Here's one that works (in the sense of being mathematically accurate)
> for all the values I've tried, but it is only about half as fast as
> nround, or 3/4 as fast as sprintf.

Again, there is no "mathematically accurate" solution in the critical
case (where both possible values are equally far off). However, your
solution now differs from sprintf when rounding 0.5 to an integer:
sprintf says 0, stround says 1.

The point of speed is probably moot now. The next Perl will include
Ilya's patch, if the creeks don't rise, and that'll be it for Perl
implementations of rounding.

> sub stround {
> my (\$n, \$places) = (@_,0);
> my \$sign = (\$n < 0) ? '-' : '';
> my \$abs = abs(\$n);
> \$sign . substr(\$abs+('0.'.'0'x\$places.'5'),
> 0, \$places+length(int(\$abs))+1);
> }

Hmm... I can't say that I like the mixture of arithmetic and text-
processing, though it may be said to be typical of Perl. If I had to
*prove* an implementation conforms to some standard, I'd prefer a numeric
solution, if possible one that corresponds step by step to the description
in the standard.

Anno

Anno Siegel, Nov 4, 2003
16. ### Roy JohnsonGuest

-berlin.de (Anno Siegel) wrote in message news:<bo8rm2\$n4\$-Berlin.DE>...
> What is the mathematically accurate result of rounding 0.05 to one
> decimal place?

Convention says it rounds up. Maybe that's not mathematics, per se.
Maybe it's not even a universal convention, but it's the only one I
ever heard.

> However, your solution now differs from sprintf when rounding 0.5 to
> an integer: sprintf says 0, stround says 1.

I'm actually happy with that, because it's consistent and
conventional. sprintf's inconsistent rounding is lamented in the FAQ.

> If I had to *prove* an implementation conforms to some standard, I'd
> prefer a numeric solution

Except for the fact that numeric representation is the problem. Using
the strings is the workaround. As an internal number, there may be no
such thing as exactly 3.005, but as a string, there is.

Roy Johnson, Nov 4, 2003
17. ### Guest

(Roy Johnson) wrote:
> -berlin.de (Anno Siegel) wrote in message
> news:<bo8rm2\$n4\$-Berlin.DE>...
> > What is the mathematically accurate result of rounding 0.05 to one
> > decimal place?

>
> Convention says it rounds up. Maybe that's not mathematics, per se.
> Maybe it's not even a universal convention, but it's the only one I
> ever heard.

Another popular (and better) convention is to round up if the preceding
digit is odd and down if the preceding digit is even, i.e. 0.05 -> 0.0,
while 0.15 -> 0.2.

Xho

--
Usenet Newsgroup Service New Rate! \$9.95/Month 50GB

, Nov 4, 2003
18. ### Ilya ZakharevichGuest

[A complimentary Cc of this posting was sent to
Anno Siegel
<-berlin.de>], who wrote in article <bo7nm6\$3qn\$-Berlin.DE>:
> > Hope this slightly compensates my goof with having a constant-folded
> > benchmark ;-),

>
> Well, it happens.

The real problem is that I know that it happens, so this is the first
thing I check when *somebody else* posts a benchmark. Sigh....

Ilya

Ilya Zakharevich, Nov 5, 2003
19. ### Anno SiegelGuest

Roy Johnson <> wrote in comp.lang.perl.misc:
> -berlin.de (Anno Siegel) wrote in message
> news:<bo8rm2\$n4\$-Berlin.DE>...
> > What is the mathematically accurate result of rounding 0.05 to one
> > decimal place?

>
> Convention says it rounds up. Maybe that's not mathematics, per se.
> Maybe it's not even a universal convention, but it's the only one I
> ever heard.

The convention has the disadvantage that it's biased. Positive numbers
will on average become larger and negatives become smaller.

> > However, your solution now differs from sprintf when rounding 0.5 to
> > an integer: sprintf says 0, stround says 1.

>
> I'm actually happy with that, because it's consistent and
> conventional. sprintf's inconsistent rounding is lamented in the FAQ.

It may look inconsistent, but there are reasons for that. Compilers
of mathematical tables have used it for centuries.

> > If I had to *prove* an implementation conforms to some standard, I'd
> > prefer a numeric solution

>
> Except for the fact that numeric representation is the problem. Using
> the strings is the workaround. As an internal number, there may be no
> such thing as exactly 3.005, but as a string, there is.

Numeric representation is a problem, but the purpose of rounding isn't
to show you the string you expect from looking at the (already rounded)
standard representation of the number. It must come up with a repre-
sentable approximation to the true rounded value.

Anno

Anno Siegel, Nov 5, 2003