Как вы округляете число с плавающей запятой в Perl?

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

например

1.2 = 1
1.7 = 2

12 ответов


выход perldoc -q round

Имеет ли Perl функцию round ()? Как насчет ceil () и floor ()? Тригонометрические функции?

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

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

The 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 distribution) реализует тригонометрические функции. Внутренне это использует 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

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


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

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

обновление

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

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

при этом вычислении -1.4 округляется до -1, а -1.6 до -2, и ноль не взорвется.


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

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

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

my $rounded = sprintf "%.0f", $float;

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

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

посмотреть 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 (on 32 бит машины) будет работать в значительной степени, как математические целое число. Другие номера не гарантированы.


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

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

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

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

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


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

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

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}

Ниже приведен пример из пяти различных способов суммирования значений. Первый-это наивный способ выполнения суммирования (и не удается). Вторая попытка использовать sprintf(), но он тоже терпит неудачу. Третий использует sprintf() успешно, в то время как последние два (4th & 5th) используют 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.


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

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;

мое решение для sprintf

if ($value =~ m/\d\..*5$/){
    $format =~ /.*(\d)f$/;
    if (defined ){
       my $coef = "0." . "0" x  . "05";    
            $value = $value + $coef;    
    }
}

$value = sprintf( "$format", $value );

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

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

cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log()/log(2))."\t\n";'