5

This question is similar to "dropping trailing ‘.0’ from floats", but for Perl and with a maximum number of digits after the decimal.

I'm looking for a way to convert numbers to string format, dropping any redundant '0', including not just right after the decimal. And still with a maximum number of digital, e.g. 3

The input data is floats. Desired output:

0         -> 0
0.1       -> 0.1
0.11      -> 0.11
0.111     -> 0.111
0.1111111 -> 0.111
Community
  • 1
  • 1
Brian Carlton
  • 7,545
  • 5
  • 38
  • 47

4 Answers4

19

Use the following directly:

my $s = sprintf('%.3f', $f);
$s =~ s/\.?0*$//;

print $s

...or define a subroutine to do it more generically:

sub fstr {
  my ($value,$precision) = @_;
  $precision ||= 3;
  my $s = sprintf("%.${precision}f", $value);
  $s =~ s/\.?0*$//;
  $s
}

print fstr(0) . "\n";
print fstr(1) . "\n";
print fstr(1.1) . "\n";
print fstr(1.12) . "\n";
print fstr(1.123) . "\n";
print fstr(1.12345) . "\n";
print fstr(1.12345, 2) . "\n";
print fstr(1.12345, 10) . "\n";

Prints:

0
1
1.1
1.12
1.123
1.123
1.12
1.12345
vladr
  • 65,483
  • 18
  • 129
  • 130
3

You can also use Math::Round to do this:

$ perl -MMath::Round=nearest -e 'print nearest(.001, 0.1), "\n"'
0.1
$ perl -MMath::Round=nearest -e 'print nearest(.001, 0.11111), "\n"'
0.111
Ryan Bright
  • 3,495
  • 21
  • 20
  • 2
    This solution only works for small numbers. `print` drops the fractional part or switches to scientific notation altogether after 15 digits; `nearest` can amplify any error already present in the number (e.g. rounding `111111111129995.56` to `.001` with `nearest` produces `111111111129995.58`, whereas `sprintf("%.3f", 111111111129995.56)` correctly produces `111111111129995.56`.) – vladr Mar 24 '10 at 02:45
3

You can use sprintf combined with eval.

my $num = eval sprintf('%.3f', $raw_num);

For example:

#!/usr/bin/perl 

my @num_array = (
    0, 1, 1.0, 0.1, 0.10, 0.11, 0.111, 0.1110, 0.1111111
);


for my $raw_num (@num_array) {
    my $num = eval sprintf('%.3f', $raw_num);
    print $num . "\n";
}

outputs:

0
1
1
0.1
0.1
0.11
0.111
0.111
0.111
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Alan W. Smith
  • 24,647
  • 4
  • 70
  • 96
0

This will give you the output your looking for:

sub dropTraillingZeros{
$_ = shift;
s/(\d*\.\d{3})(.*)/$1/;
s/(\d*\.\d)(00)/$1/;
s/(\d*\.\d{2})(0)/$1/;
print "$_\n";
}
dropTraillingZeros(0);
dropTraillingZeros(0.1);
dropTraillingZeros(0.11);
dropTraillingZeros(0.111);
dropTraillingZeros(0.11111111);
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
ForYourOwnGood
  • 38,822
  • 5
  • 30
  • 27