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

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

например

1.2 = 1
1.7 = 2
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
179
0
348 020
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Вы можете использовать такой модуль, как Математика :: Круглый:

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

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

my $rounded = sprintf "%.0f", $float;
Ответ принят как подходящий

Выход perldoc -q round

Does Perl have a round() function? What about ceil() and floor()? Trig functions?

Remember that int() merely truncates toward 0. For rounding to a certain number of digits, sprintf() or printf() is usually the easiest route.

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

The POSIX module (part of the standard Perl distribution) implements ceil(), floor(), and a number of other mathematical and trigonometric functions.

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

In 5.000 to 5.003 perls, trigonometry was done in the Math::Complex module. With 5.004, the Math::Trig module (part of the standard Perl distribution) implements the trigonometric functions. Internally it uses the Math::Complex module and some functions can break out from the real axis into the complex plane, for example the inverse sine of 2.

Rounding in financial applications can have serious implications, and the rounding method used should be specified precisely. In these cases, it probably pays not to trust whichever system rounding is being used by Perl, but to instead implement the rounding function you need yourself.

To see why, notice how you'll still have an issue on half-way-point alternation:

    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

Don't blame Perl. It's the same as in C. IEEE says we have to do this. Perl numbers whose absolute values are integers under 2**31 (on 32 bit machines) will work pretty much like mathematical integers. Other numbers are not guaranteed.

^ Тариама, почему ceil устарел? Насколько мне известно, он не устарел в POSIX или Perl. Нужна цитата!

Sam Watkins 31.10.2013 10:42

@ Новички, не пытайтесь использовать printf, если вы хотите получить результат в переменной, используйте sprintf ... надеюсь, это сэкономит вам время на отладку :-P

Boris Däppen 16.03.2016 23:29

Могу ли я использовать int() на PDL?

CinCout 20.06.2017 14:13

используйте POSIX; <br/> $ x = ($ x - этаж ($ x)> = .5)? ceil ($ x): этаж ($ x);

Joseph Argenio 08.10.2017 21:49

См. perldoc / perlfaq:

Remember that int() merely truncates toward 0. For rounding to a certain number of digits, sprintf() or printf() is usually the easiest route.

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

The POSIX module (part of the standard Perl distribution) implements ceil(), floor(), and a number of other mathematical and trigonometric functions.

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

In 5.000 to 5.003 perls, trigonometry was done in the Math::Complex module.

With 5.004, the Math::Trig module (part of the standard Perl distribution) > implements the trigonometric functions.

Internally it uses the Math::Complex module and some functions can break out from the real axis into the complex plane, for example the inverse sine of 2.

Rounding in financial applications can have serious implications, and the rounding method used should be specified precisely. In these cases, it probably pays not to trust whichever system rounding is being used by Perl, but to instead implement the rounding function you need yourself.

To see why, notice how you'll still have an issue on half-way-point alternation:

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

Don't blame Perl. It's the same as in C. IEEE says we have to do this. Perl numbers whose absolute values are integers under 2**31 (on 32 bit machines) will work pretty much like mathematical integers. Other numbers are not guaranteed.

Если вы решили использовать 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

Спасибо за указание на это. Точнее, название метода - «Округлить половину до четного».

Jean Vincent 12.09.2010 13:51

Об этом следует упомянуть во всех ответах, в которых упоминается printf или sprintf.

insaner 05.02.2016 10:56

Это крайне важная информация. У меня было несколько ошибок в программном обеспечении, потому что я предполагал, что 5 всегда будут округляться. Я наконец понял, почему Perl никогда не делал то, что я хотел. Спасибо за указание на это.

Boris Däppen 11.07.2017 16:45

На самом деле это зависит от ОС! В Windows он будет округлять половину от нуля, а в unix-подобных системах округлять половину до четного: explooringbinary.com/…

Apoc 24.07.2017 18:57

@Apoc В IEEE754 определяется как «округление до ближайшего (целого) числа связей до четного». Ближайшее - это любое целое число, которое находится на расстоянии менее 0,5 (по величине). Если случится так, что число точно на полпути (ничья), округлите до четного. Да, Windows не соответствует спецификации IEEE.

Isaac 14.08.2020 20:20

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

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

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

ОБНОВИТЬ

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

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

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

... но он не работает с отрицательными числами: еще лучше sprintf

alessandro 29.10.2012 13:58

Ах нет, это не так. Округление отрицательного числа приближает вас к нулю, а не дальше. Чему сейчас учат в школах?

RET 30.10.2012 02:05

@RET Да, с отрицательными числами не работает. $ float = -1.4 дает 0 с помощью этого метода. В моей школе этому не учили. Помните, что int () обрезается до нуля.

fishinear 20.12.2012 13:36

@fishinear Вы правы, и меня наказали должным образом. Но я сказал «для тривиального использования». Мой ответ исправлен.

RET 21.12.2012 03:34

Для случаев, когда я просто исправлял ошибки с плавающей запятой, неспособные достичь целого числа, я использовал это модифицированное решение: my $ rounded = floor ($ float + 0.1);

HoldOffHunger 23.09.2016 22:38

Обратите внимание, что $ float = 0, это не удастся :-)

mat 15.03.2017 20:24

@mat Это исправление, которое ждали давно! Исправлено должным образом, спасибо, что указали на это.

RET 15.03.2017 22:41

Древний пост, но я не понимаю, почему код должен усложняться для негативов: $rounded = int(abs($float) + 0.5); $rounded = $float * -1 if $float < 0; намного проще и на 25% быстрее, когда я тестирую его с положительными значениями (теряет немного преимущества с отрицательными). Вы даже можете записать его в одну строку с тернарным оператором, если вам НЕОБХОДИМО: int(abs($float) + 0.5)*($float < 0 ? -1 : 1); все еще проще для понимания и быстрее ...

Ecuador 15.07.2020 13:20

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

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

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

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

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

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

Еще раз, зачем отвечать на древний вопрос сложным ответом, когда что-то вроде ответа RET работает одинаково хорошо.

Joel Berger 14.06.2011 01:51

На самом деле это не очень сложно, и ответ RET включает в себя кучу математики, которая а) теоретически рискует переполнением, б) занимает больше времени и в) без нужды вносит большую неточность fp в ваше окончательное значение. Подождите, какой из них снова сложный? ;)

cptstubing06 31.05.2013 01:51

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

Подходы в стиле 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 );

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

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

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

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

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

много читающей документации о том, как округлять числа, многие эксперты предлагают написать свои собственные процедуры округления, поскольку «стандартная» версия, предоставляемая с вашим языком, может быть недостаточно точной или содержать ошибки. Я полагаю, однако, что они говорят со многими десятичными знаками, а не только с одним, двумя или тремя. Имея это в виду, вот мое решение (хотя и не ТОЧНО, как я просил, поскольку мои потребности заключаются в отображении долларов - хотя процесс не сильно отличается).

sub asDollars($) {
  my ($cost) = @_;
  my $rv = 0;

  my $negative = 0;
  if ($cost =~ /^-/) {
    $negative = 1;
    $cost =~ s/^-//;
  }

  my @cost = split(/\./, $cost);

  # let's get the first 3 digits of $cost[1]
  my ($digit1, $digit2, $digit3) = split("", $cost[1]);
  # now, is $digit3 >= 5?
  # if yes, plus one to $digit2.
  # is $digit2 > 9 now?
  # if yes, $digit2 = 0, $digit1++
  # is $digit1 > 9 now??
  # if yes, $digit1 = 0, $cost[0]++
  if ($digit3 >= 5) {
    $digit3 = 0;
    $digit2++;
    if ($digit2 > 9) {
      $digit2 = 0;
      $digit1++;
      if ($digit1 > 9) {
        $digit1 = 0;
        $cost[0]++;
      }
    }
  }
  $cost[1] = $digit1 . $digit2;
  if ($digit1 ne "0" and $cost[1] < 10) { $cost[1] .= "0"; }

  # and pretty up the left of decimal
  if ($cost[0] > 999) { $cost[0] = commafied($cost[0]); }

  $rv = join(".", @cost);

  if ($negative) { $rv = "-" . $rv; }

  return $rv;
}

sub commafied($) {
  #*
  # to insert commas before every 3rd number (from the right)
  # positive or negative numbers
  #*
  my ($num) = @_; # the number to insert commas into!

  my $negative = 0;
  if ($num =~ /^-/) {
    $negative = 1;
    $num =~ s/^-//;
  }
  $num =~ s/^(0)*//; # strip LEADING zeros from given number!
  $num =~ s/0/-/g; # convert zeros to dashes because ... computers!

  if ($num) {
    my @digits = reverse split("", $num);
    $num = "";

    for (my $i = 0; $i < @digits; $i += 3) {
      $num .= $digits[$i];
      if ($digits[$i+1]) { $num .= $digits[$i+1]; }
      if ($digits[$i+2]) { $num .= $digits[$i+2]; }
      if ($i < (@digits - 3)) { $num .= ","; }
      if ($i >= @digits) { last; }
    }

    #$num =~ s/,$//;
    $num = join("", reverse split("", $num));
    $num =~ s/-/0/g;
  }

  if ($negative) { $num = "-" . $num; }

  return $num; # a number with commas added
  #usage: my $prettyNum = commafied(1234567890);
}

чтобы подпрограмма соответствовала вашим спецификациям, просто измените следующее: if ($digit3 >= 5) { $digit3 = 0; $digit2++; if ($digit2 > 9) { $digit2 = 0; $digit1++; if ($digit1 > 9) { $digit1 = 0; $cost[0]++; } } }, чтобы оно было: if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; }, затем просто return commafied($cost[0]);

Jarett Lloyd 07.12.2019 14:20

Другие вопросы по теме