Как в Perl ограничить количество знаков после запятой, но не оставлять нулей в конце?

Этот вопрос похож на "удаление конечного '.0' из поплавков", но для Perl и с максимальным количеством знаков после запятой.

Я ищу способ преобразовать числа в строковый формат, отбрасывая любой избыточный «0», в том числе не сразу после десятичного числа. И еще с максимальным количеством цифровых, т.е. 3

Входные данные - это числа с плавающей запятой. Желаемый результат:

0         -> 0
0.1       -> 0.1
0.11      -> 0.11
0.111     -> 0.111
0.1111111 -> 0.111

person Brian Carlton    schedule 20.02.2009    source источник
comment
Эй, отличный вопрос, я много раз думал об этом! Просто мысль: примите более одобренный сообществом ответ!   -  person HoldOffHunger    schedule 10.05.2020


Ответы (4)


Для этого также можно использовать Math::Round. :

$ 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
person Ryan Bright    schedule 21.02.2009
comment
Это решение работает только для небольших чисел. print отбрасывает дробную часть или полностью переключается на экспоненциальное представление после 15 цифр; nearest может усилить любую ошибку, уже присутствующую в числе (например, округление 111111111129995.56 до .001 с nearest дает 111111111129995.58, тогда как sprintf("%.3f", 111111111129995.56) правильно дает 111111111129995.56.) - person vladr; 24.03.2010

Используйте следующее напрямую:

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

print $s

...или определите подпрограмму, чтобы сделать это в более общем виде:

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";

Отпечатки:

0
1
1.1
1.12
1.123
1.123
1.12
1.12345
person vladr    schedule 20.02.2009

Вы можете использовать sprintf в сочетании с eval.

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

Например:

#!/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";
}

выходы:

0
1
1
0.1
0.1
0.11
0.111
0.111
0.111
person Alan W. Smith    schedule 12.02.2012

Это даст вам результат, который вы ищете:

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);
person ForYourOwnGood    schedule 20.02.2009