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





Вы можете использовать такой модуль, как Математика :: Круглый:
use Math::Round;
my $rounded = round( $float );
Или вы можете сделать это грубо:
my $rounded = sprintf "%.0f", $float;
Does Perl have a round() function? What about ceil() and floor()? Trig functions?Remember that
int()merely truncates toward0. For rounding to a certain number of digits,sprintf()orprintf()is usually the easiest route.printf("%.3f", 3.1415926535); # prints 3.142The
POSIXmodule (part of the standard Perl distribution) implementsceil(),floor(), and a number of other mathematical and trigonometric functions.use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3In 5.000 to 5.003 perls, trigonometry was done in the
Math::Complexmodule. With 5.004, theMath::Trigmodule (part of the standard Perl distribution) implements the trigonometric functions. Internally it uses theMath::Complexmodule 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.0Don'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 ... надеюсь, это сэкономит вам время на отладку :-P
Могу ли я использовать int() на PDL?
используйте POSIX; <br/> $ x = ($ x - этаж ($ x)> = .5)? ceil ($ x): этаж ($ x);
См. perldoc / perlfaq:
Remember that
int()merely truncates toward 0. For rounding to a certain number of digits,sprintf()orprintf()is usually the easiest route.printf("%.3f",3.1415926535); # prints 3.142The
POSIXmodule (part of the standard Perl distribution) implementsceil(),floor(), and a number of other mathematical and trigonometric functions.use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3In 5.000 to 5.003 perls, trigonometry was done in the
Math::Complexmodule.With 5.004, the
Math::Trigmodule (part of the standard Perl distribution) > implements the trigonometric functions.Internally it uses the
Math::Complexmodule 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.0Don'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
Спасибо за указание на это. Точнее, название метода - «Округлить половину до четного».
Об этом следует упомянуть во всех ответах, в которых упоминается printf или sprintf.
Это крайне важная информация. У меня было несколько ошибок в программном обеспечении, потому что я предполагал, что 5 всегда будут округляться. Я наконец понял, почему Perl никогда не делал то, что я хотел. Спасибо за указание на это.
На самом деле это зависит от ОС! В Windows он будет округлять половину от нуля, а в unix-подобных системах округлять половину до четного: explooringbinary.com/…
@Apoc В IEEE754 определяется как «округление до ближайшего (целого) числа связей до четного». Ближайшее - это любое целое число, которое находится на расстоянии менее 0,5 (по величине). Если случится так, что число точно на полпути (ничья), округлите до четного. Да, Windows не соответствует спецификации IEEE.
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
Ах нет, это не так. Округление отрицательного числа приближает вас к нулю, а не дальше. Чему сейчас учат в школах?
@RET Да, с отрицательными числами не работает. $ float = -1.4 дает 0 с помощью этого метода. В моей школе этому не учили. Помните, что int () обрезается до нуля.
@fishinear Вы правы, и меня наказали должным образом. Но я сказал «для тривиального использования». Мой ответ исправлен.
Для случаев, когда я просто исправлял ошибки с плавающей запятой, неспособные достичь целого числа, я использовал это модифицированное решение: my $ rounded = floor ($ float + 0.1);
Обратите внимание, что $ float = 0, это не удастся :-)
@mat Это исправление, которое ждали давно! Исправлено должным образом, спасибо, что указали на это.
Древний пост, но я не понимаю, почему код должен усложняться для негативов: $rounded = int(abs($float) + 0.5); $rounded = $float * -1 if $float < 0; намного проще и на 25% быстрее, когда я тестирую его с положительными значениями (теряет немного преимущества с отрицательными). Вы даже можете записать его в одну строку с тернарным оператором, если вам НЕОБХОДИМО: int(abs($float) + 0.5)*($float < 0 ? -1 : 1); все еще проще для понимания и быстрее ...
Ниже приведены примеры пяти различных способов суммирования значений. Первый - это наивный способ суммирования (и он не работает). Вторая попытка использовать 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 работает одинаково хорошо.
На самом деле это не очень сложно, и ответ RET включает в себя кучу математики, которая а) теоретически рискует переполнением, б) занимает больше времени и в) без нужды вносит большую неточность fp в ваше окончательное значение. Подождите, какой из них снова сложный? ;)
Отрицательные числа могут добавить некоторые причуды, о которых нужно знать людям.
Подходы в стиле 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]);
^ Тариама, почему ceil устарел? Насколько мне известно, он не устарел в POSIX или Perl. Нужна цитата!