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

StackOverflow https://stackoverflow.com/questions/178539

  •  05-07-2019
  •  | 
  •  

Вопрос

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

например

1.2 = 1
1.7 = 2
Это было полезно?

Решение

Выход 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 Модуль и некоторые функции могут вырваться от реальной оси в сложную плоскость, например, обратную синус.

Округление в финансовых приложениях может иметь серьезные последствия, и используемый метод округления должен быть точно указан.В этих случаях, вероятно, стоит не доверять какой -либо системе, округлению, используемой 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, и ноль не взрывается.

Вы можете использовать модуль типа Математика::Раунд:

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 перлов тригонометрия выполнялась в Math::Complex модуль.

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

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

Округление в финансовых приложениях может иметь серьезные последствия, и используемый метод округления должен быть точно указан.В этих случаях, вероятно, стоит не доверять какой -либо системе, округлению, используемой 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 -битных машинах), будут работать в значительной степени как математические целые числа.Другие номера не гарантируются.

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

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

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

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

Что это значит, так это пройтись по каждому положительному числу в элементе, распечатать число и округленное целое число в указанном вами формате.Код объединяет соответствующие округленные положительные целые числа только на основе десятичных знаков.int($_) в основном округлить число так ($-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() успешно, в то время как последние два (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.

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

printfПодходы в стиле -style дают нам правильные цифры, но могут привести к странному отображению.Мы обнаружили, что этот метод (на мой взгляд, глупо) вводит - подпишите, должно это или не должно.Например, -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 $1){
       my $coef = "0." . "0" x $1 . "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($1)/log(2))."\t$2\n";' 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top