Rounding a float in Perl?

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

  1. jon rogers

    jon rogers Guest

    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)?

    Thanks for your time,

    JR
    jon rogers, Oct 27, 2003
    #1
    1. Advertising

  2. jon rogers

    Anno Siegel Guest

    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
    #2
    1. Advertising

  3. jon rogers

    Bart Lateur Guest

    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
    #3
  4. 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
    #4
  5. jon rogers

    Anno Siegel Guest

    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
    #5
  6. jon rogers

    Bart Lateur Guest

    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
    #6
  7. jon rogers

    Anno Siegel Guest

    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
    #7
  8. 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
    #8
  9. [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
    #9
  10. jon rogers

    Anno Siegel Guest

    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
    #10
  11. [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.


    My bad. My test had sprintf() constant-folded. After modifying it, I
    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
    #11
  12. [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
    #12
  13. jon rogers

    Anno Siegel Guest

    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
    #13
  14. jon rogers

    Roy Johnson Guest

    -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
    #14
  15. jon rogers

    Anno Siegel Guest

    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
    #15
  16. jon rogers

    Roy Johnson Guest

    -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
    #16
  17. jon rogers

    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

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service New Rate! $9.95/Month 50GB
    , Nov 4, 2003
    #17
  18. [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
    #18
  19. jon rogers

    Anno Siegel Guest

    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
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Shea Martin
    Replies:
    7
    Views:
    3,552
    P.J. Plauger
    Oct 30, 2003
  2. bd
    Replies:
    0
    Views:
    608
  3. Replies:
    13
    Views:
    825
    Dik T. Winter
    May 15, 2006
  4. Shirsoft
    Replies:
    13
    Views:
    600
    Jerry Coffin
    Feb 10, 2007
  5. Carsten Fuchs
    Replies:
    45
    Views:
    1,509
    James Kanze
    Oct 8, 2009
Loading...

Share This Page