Risposte:
Uscita di perldoc -q round
Perl ha una funzione round ()? Che dire di ceil () e floor ()? Funzioni Trig?Ricorda che
int()
si tronca semplicemente verso0
. Per arrotondare a un certo numero di cifre,sprintf()
o diprintf()
solito è il percorso più semplice.
printf("%.3f", 3.1415926535); # prints 3.142
Il
POSIX
modulo (parte della distribuzione standard di Perl) implementaceil()
,floor()
e una serie di altre funzioni matematiche e trigonometriche.
use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
In 5.000 a 5.003 perl, la trigonometria è stata eseguita nel
Math::Complex
modulo. Con 5.004, ilMath::Trig
modulo (parte della distribuzione standard Perl) implementa le funzioni trigonometriche. Internamente utilizza ilMath::Complex
modulo e alcune funzioni possono passare dall'asse reale al piano complesso, ad esempio il seno inverso di 2.L'arrotondamento nelle applicazioni finanziarie può avere serie implicazioni e il metodo di arrotondamento utilizzato deve essere specificato con precisione. In questi casi, probabilmente vale la pena non fidarsi di qualsiasi arrotondamento di sistema utilizzato da Perl, ma invece di implementare la funzione di arrotondamento necessaria.
Per capire perché, nota come avrai ancora un problema con l'alternanza a metà percorso:
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
Non dare la colpa a Perl. È lo stesso di C. IEEE dice che dobbiamo farlo. I numeri Perl i cui valori assoluti sono numeri interi sotto
2**31
(su macchine a 32 bit) funzioneranno praticamente come numeri matematici. Altri numeri non sono garantiti.
printf
se vuoi il risultato in una variabile, usa sprintf
... spero che questo ti risparmi un po 'di tempo di debug :-P
int()
su PDL?
Pur non essendo in disaccordo con le risposte complesse sui marchi a metà strada e così via, per il caso d'uso più comune (e forse banale):
my $rounded = int($float + 0.5);
AGGIORNARE
Se è possibile che tu $float
sia negativo, la seguente variazione produrrà il risultato corretto:
my $rounded = int($float + $float/abs($float*2 || 1));
Con questo calcolo -1.4 viene arrotondato a -1 e -1.6 a -2 e lo zero non esploderà.
Puoi usare un modulo come Math :: Round :
use Math::Round;
my $rounded = round( $float );
Oppure puoi farlo nel modo rozzo:
my $rounded = sprintf "%.0f", $float;
Se decidi di usare printf o sprintf, nota che usano la metà arrotondata per uniformare il metodo.
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
Vedi perldoc / perlfaq :
Ricorda che
int()
si tronca semplicemente verso 0. Per arrotondare a un certo numero di cifre,sprintf()
o diprintf()
solito è il percorso più semplice.printf("%.3f",3.1415926535); # prints 3.142
Il
POSIX
modulo (parte della distribuzione standard di Perl) implementaceil()
,floor()
e una serie di altre funzioni matematiche e trigonometriche.use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
In 5.000 a 5.003 perl, la trigonometria è stata eseguita nel
Math::Complex
modulo.Con 5.004, il
Math::Trig
modulo (parte della distribuzione standard Perl)> implementa le funzioni trigonometriche.Internamente utilizza il
Math::Complex
modulo e alcune funzioni possono passare dall'asse reale al piano complesso, ad esempio il seno inverso di 2.L'arrotondamento nelle applicazioni finanziarie può avere serie implicazioni e il metodo di arrotondamento utilizzato deve essere specificato con precisione. In questi casi, probabilmente vale la pena non fidarsi di qualsiasi arrotondamento di sistema utilizzato da Perl, ma invece di implementare la funzione di arrotondamento necessaria.
Per capire perché, nota come avrai ancora un problema con l'alternanza a metà percorso:
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
Non dare la colpa a Perl. È lo stesso di C. IEEE dice che dobbiamo farlo. I numeri Perl i cui valori assoluti sono numeri interi inferiori a 2 ** 31 (su macchine a 32 bit) funzioneranno più o meno come numeri interi matematici. Altri numeri non sono garantiti.
Non è necessario alcun modulo esterno.
$x[0] = 1.2;
$x[1] = 1.7;
foreach (@x){
print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
print "\n";
}
Forse mi manca il punto, ma ho pensato che fosse un modo molto più pulito di fare lo stesso lavoro.
Ciò che fa è scorrere tutti i numeri positivi nell'elemento, stampare il numero e il numero intero arrotondato nel formato menzionato. Il codice concatena il rispettivo intero positivo arrotondato solo in base ai decimali. int ($ _) sostanzialmente arrotondano il numero in modo che ($ -int ($ )) acquisisca i decimali. Se i decimali sono (per definizione) strettamente inferiori a 0,5, arrotondare per eccesso il numero. In caso contrario, arrotondare per eccesso aggiungendo 1.
Di seguito è riportato un esempio di cinque diversi modi per sommare i valori. Il primo è un modo ingenuo per eseguire la somma (e non riesce). Il secondo tenta di utilizzare sprintf()
, ma anche questo fallisce. Il terzo usa sprintf()
con successo mentre gli ultimi due (4 ° e 5 °) usano 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
Si noti che floor($value + 0.5)
può essere sostituito con int($value + 0.5)
per rimuovere la dipendenza POSIX
.
I numeri negativi possono aggiungere alcune stranezze di cui le persone devono essere consapevoli.
printf
gli approcci in stile ci danno numeri corretti, ma possono comportare delle visualizzazioni dispari. Abbiamo scoperto che questo metodo (secondo me, stupidamente) mette un -
segno se dovrebbe o non dovrebbe. Ad esempio, -0,01 arrotondato al primo decimale restituisce un -0,0, anziché solo 0. Se hai intenzione di seguire l' printf
approccio di stile e sai che non vuoi un decimale, usa %d
e no %f
(quando hai bisogno di decimali, è quando il display diventa traballante).
Mentre è corretto e per la matematica non è un grosso problema, per la visualizzazione sembra strano mostrare qualcosa come "-0.0".
Per il metodo int, i numeri negativi possono cambiare ciò che si desidera di conseguenza (anche se ci sono alcuni argomenti che possono essere fatti, sono corretti).
La int + 0.5
causa problemi reali con i numeri -negative, se non volete farlo funzionare in questo modo, ma immagino la maggior parte delle persone non lo fanno. -0.9 probabilmente dovrebbe arrotondare a -1, non a 0. Se sai che vuoi che il negativo sia un soffitto piuttosto che un pavimento, puoi farlo in una riga, altrimenti potresti voler usare il metodo int con un minore modifica (questo ovviamente funziona solo per recuperare numeri interi:
my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
Se ti preoccupi solo di ottenere un valore intero da un numero intero in virgola mobile (ovvero 12347.9999 o 54321.0001), questo approccio (preso in prestito e modificato dall'alto) farà il trucco:
my $rounded = floor($float + 0.1);
un sacco di leggere la documentazione su come arrotondare i numeri, molti esperti suggeriscono di scrivere le proprie routine di arrotondamento, poiché la versione "in scatola" fornita con la propria lingua potrebbe non essere abbastanza precisa o contenere errori. immagino, tuttavia, stiano parlando di molti decimali non solo uno, due o tre. con questo in mente, ecco la mia soluzione (sebbene non ESATTAMENTE come richiesto in quanto le mie esigenze sono di visualizzare dollari - il processo non è molto diverso, però).
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]++; } } }
quindi è: if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; }
quindi soloreturn commafied($cost[0]);
cat table |
perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";'