Как я могу округлить десятичное число (с плавающей запятой) до ближайшего целого?
например.
1.2 = 1
1.7 = 2
Как я могу округлить десятичное число (с плавающей запятой) до ближайшего целого?
например.
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 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-разрядные машины) будут работать почти так же, как математические целые числа. Другие номера не гарантируются.
Не соглашаясь с комплексными ответами о методах полупунктов и т.д., для более распространенного (и, возможно, тривиального) варианта использования:
my $rounded = int($float + 0.5);
UPDATE
Если возможно, что ваш $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, обратите внимание, что они используют метод Round half to even.
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 ($ _) в основном округляет число так ($ -int ($)) фиксирует десятичные числа. Если десятичные знаки (по определению) строго меньше 0,5, округлите число. Если нет, округлите, добавив 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
.
Отрицательные числа могут добавить некоторые причуды, о которых люди должны знать.
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;
Далее будут округлены положительные или отрицательные числа до заданной десятичной позиции:
sub round ()
{
my ($x, $pow10) = @_;
my $a = 10 ** $pow10;
return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}
Мое решение для 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";'