Popcorn Dave has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I'm in the process of writing a stock tracking program that uses sprintf to display the percent change in a given stock to 3 decimal places. However when I use sprintf "%.3f", if my number is 0.010, I'm getting the trailing 0 like I expect since I've set it to 3 decimal places.

What I'd like to do is to get rid of trailing zeros. I've looked over the docs on sprintf and can't find a way to do it. My solution at present is the following:

$percent_change = sprintf "%.3f", $percent_change; $percent_change =~ s/0+$// if $percent_change =~ /0+$/;

That code works, but it seems a bit of a workaround. Have I missed something somewhere in my use of sprintf?

There is no emoticon for what I'm feeling now.

Replies are listed 'Best First'.
Re: Deleting trailing 0's after using sprintf
by davido (Cardinal) on Mar 19, 2004 at 07:20 UTC
    I hesitated posting this at first because I felt that the 'sprintf "%.3g", $string;' solution already proposed was cleaner.

    However, the behavior where 'g' suppresses trailing floating-point zeros seems to not be documented in the POD, which leads me to believe it's a compiler-dependant behavior (dependant on the compiler on which Perl was built) rather than clearly defined Perl behavior.

    Of course someone else also already suggested a my $val = +sprintf "%.3f", $string; solution, which also will suppress trailing zeros. That also seemed pretty neat, as it relies on the fact that the + operator forces string-to-number coersion, which causes trailing zeros to be forgotten. ...a great solution.

    However, if it turns out that 'g' suppressing trailing fp zeros is undefined (and possibly unreliable) behavior, and if for some reason you choose not to take advantage of Perl's ability to coerce strings into numeric values easily, the following solution may have merit instead.

    use strict; use warnings; use Math::Round; my $test_value = 100.510; $test_value = fp_round( $test_value, 3 ); print "$test_value\n"; sub fp_round { my ( $val, $places ) = @_; return round( $val * ( 10 ** $places ) ) / ( 10 ** $places ); }

    ...Just another way to do it, I suppose.

    All the OP is really trying to do is just round to a specific number of floating point places. Math::Round will round to the nearest integer. But that's easy to improve upon with a little math, as demonstrated in my snippet.


    Dave

Re: Deleting trailing 0's after using sprintf
by jweed (Chaplain) on Mar 19, 2004 at 06:39 UTC
    This is actually an exercise in Jeff Friedl's Mastering Regular Expressions.
    His final reccomendation is to skip the sprintf alltogether and just use
    $percent_change =~ s/(\.\d\d(?>[1-9]?))\d+/$1/
    Which always keeps 2 digits, i.e. 2.500 => 2.50.
    If you don't wan that to happen, use
    $percent_change =~ s/(\.\d(?>[1-9]{0,2}))\d+/$1/



    Code is (almost) always untested.
    http://www.justicepoetic.net/
Re: Deleting trailing 0's after using sprintf
by kvale (Monsignor) on Mar 19, 2004 at 06:41 UTC
    The g flag excises trailing zeros:
    my $pct = 10.300; $pct = sprintf "%.3g", $pct; print "$pct\n"; __OUTPUT__ 10.3

    -Mark

Re: Deleting trailing 0's after using sprintf
by BUU (Prior) on Mar 19, 2004 at 06:33 UTC
    Minor nitpick: $percent_change =~ s/0+$// if $percent_change =~ /0+$/; the modifying if statement is redundant, the s/// operator only replaces if it matches in the first place.
      Very good point. My logic was correct, but I did have one to many tests. Thanks for pointing that out!

      There is no emoticon for what I'm feeling now.

Re: Deleting trailing 0's after using sprintf
by cLive ;-) (Prior) on Mar 19, 2004 at 06:59 UTC
    I'd use :
    $percent_change = 0+sprintf "%.3f", $percent_change;

    .02

    cLive ;-)