Odpowiedzi:
Wyjście perldoc -q round
Czy Perl ma funkcję round ()? A co z ceil () i floor ()? Funkcje wyzwalania?Pamiętaj, że
int()
po prostu skraca się w kierunku0
. Zaokrągla do określonej liczby cyfrsprintf()
lubprintf()
jest zwykle najłatwiejszą trasą.
printf("%.3f", 3.1415926535); # prints 3.142
POSIX
Moduł (część standardowego rozkładu Perl) narzędziaceil()
,floor()
oraz szereg innych funkcji matematycznych i trygonometrycznych.
use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
Od 5000 do 5,003 perlów trygonometria została wykonana w
Math::Complex
module. W 5.004Math::Trig
moduł (część standardowej dystrybucji Perla) implementuje funkcje trygonometryczne. Wewnętrznie wykorzystujeMath::Complex
moduł, a niektóre funkcje mogą wyłamać się z rzeczywistej osi na płaszczyznę zespoloną, na przykład odwrotny sinus 2.Zaokrąglanie wniosków finansowych może mieć poważne konsekwencje, dlatego należy dokładnie określić zastosowaną metodę zaokrąglania. W takich przypadkach prawdopodobnie opłaca się nie ufać jakimkolwiek zaokrągleniom systemowym używanym przez Perl, ale zamiast tego zaimplementować funkcję zaokrąglania, której potrzebujesz.
Aby zobaczyć, dlaczego, zwróć uwagę, że nadal będziesz mieć problem z przemianą w połowie punktu:
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
Nie wiń Perla. Jest tak samo jak w C. IEEE mówi, że musimy to zrobić. Liczby Perla, których wartości bezwzględne są liczbami całkowitymi poniżej
2**31
(na komputerach 32-bitowych), będą działać podobnie jak matematyczne liczby całkowite. Inne numery nie są gwarantowane.
printf
jeśli chcesz, aby wynik był w zmiennej, użyj sprintf
...
int()
na PDL?
Nie zgadzając się ze złożonymi odpowiedziami dotyczącymi znaków w połowie i tak dalej, dla bardziej powszechnego (i być może trywialnego) przypadku użycia:
my $rounded = int($float + 0.5);
AKTUALIZACJA
Jeśli możliwe, że twój wynik $float
będzie ujemny, następująca zmiana da prawidłowy wynik:
my $rounded = int($float + $float/abs($float*2 || 1));
Przy takim obliczeniu -1,4 jest zaokrąglane do -1, a od -1,6 do -2, a zero nie eksploduje.
Możesz użyć modułu takiego jak Math :: Round :
use Math::Round;
my $rounded = round( $float );
Lub możesz to zrobić w brutalny sposób:
my $rounded = sprintf "%.0f", $float;
Jeśli zdecydujesz się użyć printf lub sprintf, pamiętaj, że używają one metody 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
Zobacz perldoc / perlfaq :
Pamiętaj, że
int()
po prostu skraca się w kierunku 0. Zaokrągla do określonej liczby cyfrsprintf()
lubprintf()
jest zwykle najłatwiejszą trasą.printf("%.3f",3.1415926535); # prints 3.142
POSIX
Moduł (część standardowego rozkładu Perl) narzędziaceil()
,floor()
oraz szereg innych funkcji matematycznych i trygonometrycznych.use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
Od 5000 do 5,003 perlów trygonometria została wykonana w
Math::Complex
module.W 5.004
Math::Trig
moduł (część standardowej dystrybucji Perla)> implementuje funkcje trygonometryczne.Wewnętrznie wykorzystuje
Math::Complex
moduł, a niektóre funkcje mogą wyłamać się z rzeczywistej osi na płaszczyznę zespoloną, na przykład odwrotny sinus 2.Zaokrąglanie wniosków finansowych może mieć poważne konsekwencje, dlatego należy dokładnie określić zastosowaną metodę zaokrąglania. W takich przypadkach prawdopodobnie opłaca się nie ufać jakimkolwiek zaokrągleniom systemowym używanym przez Perl, ale zamiast tego zaimplementować funkcję zaokrąglania, której potrzebujesz.
Aby zobaczyć, dlaczego, zwróć uwagę, że nadal będziesz mieć problem z przemianą w połowie punktu:
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
Nie wiń Perla. Jest tak samo jak w C. IEEE mówi, że musimy to zrobić. Liczby Perla, których wartości bezwzględne są liczbami całkowitymi poniżej 2 ** 31 (na komputerach 32-bitowych), będą działać podobnie jak matematyczne liczby całkowite. Inne numery nie są gwarantowane.
Nie potrzebujesz żadnego zewnętrznego modułu.
$x[0] = 1.2;
$x[1] = 1.7;
foreach (@x){
print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
print "\n";
}
Być może nie rozumiem, ale pomyślałem, że to znacznie czystszy sposób na wykonanie tej samej pracy.
To co robi to przejście przez każdą liczbę dodatnią w elemencie, wypisanie liczby i zaokrąglonej liczby całkowitej we wspomnianym formacie. Kod łączy odpowiednie zaokrąglone dodatnie liczby całkowite tylko na podstawie liczb dziesiętnych. int ($ _) zasadniczo zaokrągla liczbę w dół, więc ($ -int ($ )) przechwytuje ułamki dziesiętne. Jeśli liczby dziesiętne są (z definicji) dokładnie mniejsze niż 0,5, zaokrąglij liczbę w dół. Jeśli nie, zaokrąglij w górę, dodając 1.
Poniżej znajduje się próbka pięciu różnych sposobów sumowania wartości. Pierwsza to naiwny sposób wykonania sumowania (i zawodzi). Druga próba użycia sprintf()
, ale też się nie udaje. Trzeci używa sprintf()
pomyślnie, podczas gdy ostatnie dwa (czwarte i piąte) używają 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
Zwróć uwagę, że floor($value + 0.5)
można go zastąpić, int($value + 0.5)
aby usunąć zależność od POSIX
.
Liczby ujemne mogą dodawać pewne dziwactwa, o których ludzie muszą wiedzieć.
printf
Podejścia w stylu dają nam prawidłowe liczby, ale mogą powodować dziwne wyświetlanie. Odkryliśmy, że ta metoda (moim zdaniem głupio) stawia -
znak, czy powinna, czy nie. Na przykład, -0,01 zaokrąglone do jednego miejsca po przecinku zwraca -0,0, a nie tylko 0. Jeśli masz zamiar zastosować printf
podejście stylowe i wiesz, że nie chcesz, użyj %d
i nie %f
(jeśli potrzebujesz miejsc po przecinku, wtedy wyświetlacz staje się niewyraźny).
Chociaż jest poprawny i dla matematyki nic wielkiego, w przypadku wyświetlania wygląda po prostu dziwnie, pokazując coś w rodzaju „-0,0”.
W przypadku metody int liczby ujemne mogą w rezultacie zmienić to, co chcesz (chociaż istnieje kilka argumentów, które można uczynić, że są poprawne).
int + 0.5
Powoduje rzeczywiste problemy z numerami -wykluczające, chyba że chcesz to działa w ten sposób, ale wyobrażam sobie, że większość ludzi nie. -0,9 powinno prawdopodobnie zaokrąglić do -1, a nie 0. Jeśli wiesz, że chcesz, aby wartość ujemna była sufitem, a nie podłogą, możesz to zrobić w jednej linii, w przeciwnym razie możesz chcieć użyć metody int z drugorzędnym modyfikacja (to oczywiście działa tylko w celu odzyskania liczb całkowitych:
my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
Jeśli zależy ci tylko na uzyskaniu wartości całkowitej z całej liczby zmiennoprzecinkowej (tj. 12347,9999 lub 54321,0001), to podejście (pożyczone i zmodyfikowane z góry) załatwi sprawę:
my $rounded = floor($float + 0.1);
mnóstwo dokumentacji na temat zaokrąglania liczb, wielu ekspertów sugeruje pisanie własnych procedur zaokrąglania, ponieważ wersja „gotowa” dostarczona z Twoim językiem może nie być wystarczająco dokładna lub zawierać błędy. wyobrażam sobie jednak, że mówią o wielu miejscach po przecinku, a nie tylko o jednym, dwóch lub trzech. mając to na uwadze, oto moje rozwiązanie (chociaż nie DOKŁADNIE zgodnie z wymaganiami, ponieważ moje potrzeby to wyświetlanie dolarów - proces nie różni się jednak zbytnio).
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]++; } } }
więc jest: if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; }
wtedy po prostureturn commafied($cost[0]);
cat table |
perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";'