Відповіді:
Вихід perldoc -q round
Чи Perl має круглу () функцію? А що з стелею () та підлогою ()? Триггерні функції?Пам’ятайте, що
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.004Math::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
Не звинувачуй Перл. Це те саме, що в C. IEEE каже, що ми повинні це зробити. Числа Perl, абсолютні значення яких є цілими числами
2**31
(на 32-бітових машинах), будуть працювати приблизно так само, як математичні цілі числа. Інші номери не гарантуються.
printf
якщо ви хочете, щоб результат був змінним, використовуйте sprintf
... сподіваюся, це заощадить вам час налагодження :-P
int()
на PDL?
Хоча не погоджуючись зі складними відповідями про півдорогі позначки тощо, для більш поширеного (і, можливо, тривіального) випадку використання:
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 перлів у
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
Не звинувачуй Перл. Це те саме, що в 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
-стильові підходи дають нам правильні цифри, але вони можуть спричинити за собою кілька непарних показів. Ми виявили, що цей метод (на мою думку, нерозумно) дає -
знак, чи слід чи не слід. Наприклад, -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;
Якщо ви переймаєтесь тим, як отримати ціле значення з цілого числа плаваючої точки (тобто 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]);
cat table |
perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";'