Respuestas:
Salida de perldoc -q round
¿Perl tiene una función round ()? ¿Qué pasa con ceil () y floor ()? ¿Funciones de disparo?Recuerde que
int()
simplemente se trunca hacia0
. Para redondear a un cierto número de dígitos,sprintf()
oprintf()
suele ser la ruta más fácil.
printf("%.3f", 3.1415926535); # prints 3.142
El
POSIX
módulo (parte de la distribución estándar de Perl) implementosceil()
,floor()
y una serie de otras funciones matemáticas y trigonométricas.
use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
En 5.000 a 5.003 perls, la trigonometría se realizó en el
Math::Complex
módulo. Con 5.004, elMath::Trig
módulo (parte de la distribución estándar de Perl) implementa las funciones trigonométricas. Internamente usa elMath::Complex
módulo y algunas funciones pueden salir del eje real al plano complejo, por ejemplo, el seno inverso de 2.El redondeo en las aplicaciones financieras puede tener serias implicaciones, y el método de redondeo utilizado debe especificarse con precisión. En estos casos, probablemente valga la pena no confiar en el sistema de redondeo que utiliza Perl, sino implementar la función de redondeo que usted necesita.
Para ver por qué, observe cómo todavía tendrá un problema con la alternancia de punto medio:
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
No culpes a Perl. Es lo mismo que en C. IEEE dice que tenemos que hacer esto. Los números de Perl cuyos valores absolutos son enteros debajo
2**31
(en máquinas de 32 bits) funcionarán más o menos como enteros matemáticos. Otros números no están garantizados.
printf
si desea el resultado en una variable, use sprintf
... espero que esto le ahorre algo de tiempo de depuración
int()
en PDL?
Si bien no está en desacuerdo con las respuestas complejas sobre las marcas a mitad de camino, etc., para el caso de uso más común (y posiblemente trivial):
my $rounded = int($float + 0.5);
ACTUALIZAR
Si es posible $float
que sea negativo, la siguiente variación producirá el resultado correcto:
my $rounded = int($float + $float/abs($float*2 || 1));
Con este cálculo, -1.4 se redondea a -1 y -1.6 a -2, y el cero no explotará.
Puedes usar un módulo como Math :: Round :
use Math::Round;
my $rounded = round( $float );
O puedes hacerlo de la manera cruda:
my $rounded = sprintf "%.0f", $float;
Si decide usar printf o sprintf, tenga en cuenta que usan la mitad redonda para igualar el método.
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
Ver perldoc / perlfaq :
Recuerde que
int()
simplemente se trunca hacia 0. Para redondear a un cierto número de dígitos,sprintf()
oprintf()
suele ser la ruta más fácil.printf("%.3f",3.1415926535); # prints 3.142
El
POSIX
módulo (parte de la distribución estándar de Perl) implementosceil()
,floor()
y una serie de otras funciones matemáticas y trigonométricas.use POSIX; $ceil = ceil(3.5); # 4 $floor = floor(3.5); # 3
En 5.000 a 5.003 perls, la trigonometría se realizó en el
Math::Complex
módulo.Con 5.004, el
Math::Trig
módulo (parte de la distribución estándar de Perl)> implementa las funciones trigonométricas.Internamente usa el
Math::Complex
módulo y algunas funciones pueden salir del eje real al plano complejo, por ejemplo, el seno inverso de 2.El redondeo en las aplicaciones financieras puede tener serias implicaciones, y el método de redondeo utilizado debe especificarse con precisión. En estos casos, probablemente valga la pena no confiar en el sistema de redondeo que utiliza Perl, sino implementar la función de redondeo que usted necesita.
Para ver por qué, observe cómo todavía tendrá un problema con la alternancia de punto medio:
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
No culpes a Perl. Es lo mismo que en C. IEEE dice que tenemos que hacer esto. Los números de Perl cuyos valores absolutos son enteros menores de 2 ** 31 (en máquinas de 32 bits) funcionarán más o menos como enteros matemáticos. Otros números no están garantizados.
No necesitas ningún módulo externo.
$x[0] = 1.2;
$x[1] = 1.7;
foreach (@x){
print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
print "\n";
}
Puede que me esté perdiendo su punto, pero pensé que esta era una forma mucho más limpia de hacer el mismo trabajo.
Lo que esto hace es recorrer cada número positivo en el elemento, imprimir el número y el entero redondeado en el formato que mencionó. El código concatena el entero positivo redondeado respectivo solo en función de los decimales. int ($ _) básicamente redondea el número para que ($ -int ($ )) capture los decimales. Si los decimales son (por definición) estrictamente menores que 0.5, redondee el número hacia abajo. Si no, redondee agregando 1.
A continuación se muestra una muestra de cinco formas diferentes de sumar valores. La primera es una forma ingenua de realizar la suma (y falla). El segundo intenta usarlo sprintf()
, pero también falla. El tercero usa con sprintf()
éxito mientras que los dos últimos (4to y 5to) lo usan 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
Tenga en cuenta que floor($value + 0.5)
se puede reemplazar con int($value + 0.5)
para eliminar la dependencia POSIX
.
Los números negativos pueden agregar algunas peculiaridades que las personas deben tener en cuenta.
printf
los enfoques de estilo nos dan números correctos, pero pueden dar lugar a algunas pantallas extrañas. Hemos descubierto que este método (en mi opinión, estúpidamente) pone un -
signo de si debería o no debería. Por ejemplo, -0.01 redondeado a una posición decimal devuelve un -0.0, en lugar de solo 0. Si va a hacer el printf
enfoque de estilo y sabe que no quiere decimal, use %d
y no %f
(cuando necesita decimales, es cuando el la pantalla se torna inestable).
Si bien es correcto y para las matemáticas no es gran cosa, para mostrar simplemente se ve raro mostrando algo como "-0.0".
Para el método int, los números negativos pueden cambiar lo que desea como resultado (aunque hay algunos argumentos que pueden hacerse son correctos).
Esto int + 0.5
causa problemas reales con los números negativos, a menos que quieras que funcione de esa manera, pero imagino que la mayoría de las personas no lo hacen. -0.9 probablemente debería redondearse a -1, no a 0. Si sabe que desea que lo negativo sea un techo en lugar de un piso, puede hacerlo de una sola línea, de lo contrario, es posible que desee utilizar el método int con un menor modificación (esto obviamente solo funciona para recuperar números enteros:
my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
Si solo le interesa obtener un valor entero de un número entero de coma flotante (es decir, 12347.9999 o 54321.0001), este enfoque (prestado y modificado desde arriba) hará el truco:
my $rounded = floor($float + 0.1);
Muchos expertos sugieren escribir sus propias rutinas de redondeo, ya que la versión 'enlatada' proporcionada con su idioma puede no ser lo suficientemente precisa o contener errores. Sin embargo, imagino que están hablando muchos decimales, no solo uno, dos o tres. Con eso en mente, aquí está mi solución (aunque no EXACTAMENTE lo solicitado, ya que mis necesidades son mostrar dólares; sin embargo, el proceso no es muy diferente).
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]++; } } }
así es: if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; }
entonces soloreturn commafied($cost[0]);
cat table |
perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";'