Как вы округляете число с плавающей запятой в Perl? - PullRequest
167 голосов
/ 07 октября 2008

Как можно округлить десятичное число (с плавающей запятой) до ближайшего целого числа?

, например

1.2 = 1
1.7 = 2

Ответы [ 12 ]

191 голосов
/ 07 октября 2008

Выход perldoc -q round

Есть ли в Perl функция round ()? Что насчет ceil () и floor ()? Триг функции?

Помните, что int() просто усекается к 0. Для округления до определенного количества цифр, sprintf() или printf() обычно является самым простым маршрут.

    printf("%.3f", 3.1415926535);       # prints 3.142

Модуль POSIX (часть стандартного дистрибутива Perl) реализует ceil(), floor() и ряд других математических и тригонометрических функции.

    use POSIX;
    $ceil   = ceil(3.5);                        # 4
    $floor  = floor(3.5);                       # 3

В 5.000 до 5.003 перлов тригонометрия была выполнена в Math::Complex модуль. С 5.004 модуль Math::Trig (часть стандартного Perl распределение) реализует тригонометрические функции. Внутренне это использует модуль Math::Complex, и некоторые функции могут выйти из действительная ось в комплексной плоскости, например, обратный синус 2.

Округление в финансовых приложениях может иметь серьезные последствия, и используемый метод округления должен быть точно указан. В этих случаях, вероятно, стоит не доверять какому-либо системному округлению используется Perl, но для реализации функции округления вам нужно сам.

Чтобы понять почему, обратите внимание на то, что у вас все еще будет проблема с промежуточной точкой чередование:

    for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}

    0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
    0.8 0.8 0.9 0.9 1.0 1.0

Не вините Perl. Это так же, как в C. IEEE говорит, что мы должны сделать этот. Числа Perl, абсолютные значения которых являются целыми числами под 2**31 (включено 32-битные машины) будут работать почти как математические числа. Другие номера не гарантируются.

127 голосов
/ 14 августа 2009

Несмотря на несогласие со сложными ответами о промежуточных отметках и так далее, для более распространенного (и, возможно, тривиального) варианта использования:

my $rounded = int($float + 0.5);

UPDATE

Если ваш $float может быть отрицательным, следующий вариант даст правильный результат:

my $rounded = int($float + $float/abs($float*2 || 1));

При таком расчете -1,4 округляется до -1, а от -1,6 до -2, и ноль не взорвется.

71 голосов
/ 07 октября 2008

Вы можете использовать модуль типа Math :: Round :

use Math::Round;
my $rounded = round( $float );

Или вы можете сделать это грубо:

my $rounded = sprintf "%.0f", $float;
45 голосов
/ 07 октября 2008

Если вы решите использовать printf или sprintf, обратите внимание, что они используют метод Round от половины до четного .

foreach my $i ( 0.5, 1.5, 2.5, 3.5 ) {
    printf "$i -> %.0f\n", $i;
}
__END__
0.5 -> 0
1.5 -> 2
2.5 -> 2
3.5 -> 4
8 голосов
/ 07 октября 2008

См. perldoc / perlfaq :

Помните, что int() просто усекается до 0. Для округления до определенное количество цифр, sprintf() или printf() обычно Самый простой маршрут.

 printf("%.3f",3.1415926535);
 # prints 3.142

Модуль POSIX (часть стандартного дистрибутива Perl) реализует ceil(), floor() и ряд других математических и тригонометрические функции.

use POSIX;
$ceil  = ceil(3.5); # 4
$floor = floor(3.5); # 3

В 5.000 - 5.003 perls тригонометрия была выполнена в модуле Math::Complex.

С 5.004 модуль Math::Trig (часть стандартного распределения Perl)> реализует тригонометрические функции.

Внутренне он использует модуль Math::Complex, и некоторые функции могут сломаться от действительной оси в комплексную плоскость, например, обратный синус 2.

Округление в финансовых приложениях может иметь серьезные последствия, и округление используемый метод должен быть указан точно. В этих случаях, вероятно, не стоит доверяйте любой системе округления, используемой Perl, но вместо этого реализуйте Функция округления вам нужна.

Чтобы понять почему, обратите внимание, что у вас все еще будет проблема при чередовании полпути:

for ($i = 0; $i < 1.01; $i += 0.05)
{
   printf "%.1f ",$i
}

0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0

Не вините Perl. Это так же, как в C. IEEE говорит, что мы должны сделать этот. Числа Perl, абсолютные значения которых являются целыми числами до 2 ** 31 (включено 32-битные машины) будут работать почти как математические числа. Другие номера не гарантируются.

3 голосов
/ 14 июня 2011

Вам не нужен внешний модуль.

$x[0] = 1.2;
$x[1] = 1.7;

foreach (@x){
  print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
  print "\n";
}

Возможно, я упускаю вашу точку зрения, но я подумал, что это намного более чистый способ сделать ту же работу.

Для этого нужно пройти через все положительные числа в элементе, напечатать число и округленное целое число в указанном вами формате. Код объединяет соответствующее округленное положительное целое число только на основе десятичных знаков. int ($ _) в основном округление число, поэтому ($ -int ($ )) фиксирует десятичные дроби. Если десятичные дроби (по определению) строго меньше 0,5, округлите число вниз. Если нет, округлите, добавив 1.

1 голос
/ 12 сентября 2014

Следующие значения округляют положительные или отрицательные числа до заданной десятичной позиции:

sub round ()
{
    my ($x, $pow10) = @_;
    my $a = 10 ** $pow10;

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}
1 голос
/ 23 августа 2011

Отрицательные числа могут добавить некоторые причуды, о которых нужно знать людям.

Подходы в стиле

printf дают нам правильные числа, но могут привести к некоторым странным проявлениям. Мы обнаружили, что этот метод (на мой взгляд, глупо) ставит знак - независимо от того, должен он или нет. Например, -0,01, округленное до одного десятичного знака, возвращает -0,0, а не просто 0. Если вы собираетесь использовать стиль в стиле printf и знаете, что вам не нужно десятичное число, используйте %d, а не %f (когда вам нужны десятичные дроби, это когда дисплей становится шатким).

Хотя это правильно и для математики не имеет большого значения, для отображения это выглядит просто странно, показывая что-то вроде "-0.0".

Для метода int отрицательные числа могут изменить то, что вы хотите в результате (хотя есть некоторые аргументы, которые можно привести, они верны).

int + 0.5 вызывает реальные проблемы с отрицательными числами, если только вы не хотите, чтобы это работало так, но я думаю, что большинство людей этого не делают. -0.9, вероятно, должно округляться до -1, а не 0. Если вы знаете, что хотите, чтобы отрицание было потолком, а не полом, то вы можете сделать это в одной строке, в противном случае вы можете использовать метод int с второстепенным модификация (очевидно, это работает только для получения целых чисел:

my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
1 голос
/ 22 декабря 2010

Ниже приведен пример пяти различных способов суммирования значений. Первый - это наивный способ выполнить суммирование (и не удается). Второй пытается использовать sprintf(), но это тоже не удается. Третий использует sprintf() успешно, в то время как последние два (4-й и 5-й) используют floor($value + 0.5).

 use strict;
 use warnings;
 use POSIX;

 my @values = (26.67,62.51,62.51,62.51,68.82,79.39,79.39);
 my $total1 = 0.00;
 my $total2 = 0;
 my $total3 = 0;
 my $total4 = 0.00;
 my $total5 = 0;
 my $value1;
 my $value2;
 my $value3;
 my $value4;
 my $value5;

 foreach $value1 (@values)
 {
      $value2 = $value1;
      $value3 = $value1;
      $value4 = $value1;
      $value5 = $value1;

      $total1 += $value1;

      $total2 += sprintf('%d', $value2 * 100);

      $value3 = sprintf('%1.2f', $value3);
      $value3 =~ s/\.//;
      $total3 += $value3;

      $total4 += $value4;

      $total5 += floor(($value5 * 100.0) + 0.5);
 }

 $total1 *= 100;
 $total4 = floor(($total4 * 100.0) + 0.5);

 print '$total1: '.sprintf('%011d', $total1)."\n";
 print '$total2: '.sprintf('%011d', $total2)."\n";
 print '$total3: '.sprintf('%011d', $total3)."\n";
 print '$total4: '.sprintf('%011d', $total4)."\n";
 print '$total5: '.sprintf('%011d', $total5)."\n";

 exit(0);

 #$total1: 00000044179
 #$total2: 00000044179
 #$total3: 00000044180
 #$total4: 00000044180
 #$total5: 00000044180

Обратите внимание, что floor($value + 0.5) можно заменить на int($value + 0.5), чтобы удалить зависимость от POSIX.

0 голосов
/ 23 сентября 2016

Если вас интересует только получение целочисленного значения из целого числа с плавающей запятой (то есть 12347,9999 или 54321,0001), этот подход (заимствованный и измененный сверху) поможет вам:

my $rounded = floor($float + 0.1); 
...