¿Cómo se redondea un número de coma flotante en Perl?

¿Cómo puedo redondear un número decimal (punto flotante) al entero más cercano?

p.ej

1.2 = 1
1.7 = 2

preguntado Oct 07 '08, 11:10

13 Respuestas

Salida de perldoc -q round

¿Tiene Perl una función round ()? ¿Qué pasa con ceil () y floor ()? Funciones de activación?

Recuerda que int() simplemente se trunca hacia 0. Para redondear a un cierto número de dígitos, sprintf() or printf() suele ser la ruta más fácil.

    printf("%.3f", 3.1415926535);       # prints 3.142

La POSIX módulo (parte de la distribución estándar de Perl) implementa ceil(), floor()y 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, se realizó trigonometría en el Math::Complex módulo. Con 5.004, el Math::Trig El módulo (parte de la distribución estándar de Perl) implementa las funciones trigonométricas. Internamente utiliza 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 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 utilice Perl, sino implementar la función de redondeo que necesita usted mismo.

Para ver por qué, observe cómo seguirá teniendo 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. Números de Perl cuyos valores absolutos son enteros bajo 2**31 (en máquinas de 32 bits) funcionará de forma muy similar a los números enteros matemáticos. No se garantizan otros números.

Respondido 13 ago 09, 16:08

^ Thariama, ¿por qué se desaprobaría ceil? No está en desuso en POSIX o perl que yo sepa. ¡Se necesita una cita! - sam watkins

@Principiantes, no intentes usar printf si quieres el resultado en una variable, usa sprintf... espero que esto le ahorre algo de tiempo de depuración :-P - Boris Däppen

Puedo usar int() en PDL? - CinCout

use POSIX; $ x = ($ x - piso ($ x)> = .5)? ceil ($ x): piso ($ x); - José Argenio

Sin estar 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);

ACTUALIZACIÓN

Si es posible para tu $float para ser 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á.

respondido 15 mar '17, 19:03

... pero falla en números negativos: aún mejor sprintf - alessandro

Ah no, no es así. Redondear un número negativo te acerca a cero, no más lejos. ¿Qué están enseñando en las escuelas estos días? - RET

@RET Sí, falla con números negativos. $ float = -1.4 da como resultado 0 con este método. Eso no es lo que enseñaban en mi escuela. Recuerde que int () se trunca hacia cero. - Pescado

@fishinear Tienes razón y estoy debidamente castigado. Pero dije 'para un caso de uso trivial'. Mi respuesta ha sido corregida. - RET

Tenga en cuenta que $ float = 0, esto fallará :-) - estera

Puedes usar un módulo como Matemáticas :: Ronda:

use Math::Round;
my $rounded = round( $float );

O puedes hacerlo de la manera más burda:

my $rounded = sprintf "%.0f", $float;

Respondido 08 Oct 08, 07:10

Si decide utilizar printf o sprintf, tenga en cuenta que utilizan el Redondear la mitad para igualar 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

Respondido el 13 de junio de 11 a las 22:06

Gracias por señalar esto. Más precisamente, el nombre del método es 'Round Half to Even'. - Juan Vicente

Todas las respuestas que mencionan printf o sprintf deberían mencionar esto. - loco

Ésta es una información extremadamente importante. Tuve varias veces errores en el software porque asumí que 5 siempre se redondearán. Finalmente descubrí por qué Perl nunca hizo lo que yo quería. Gracias por señalar esto. - Boris Däppen

En realidad, esto depende del sistema operativo. En Windows, se redondeará a la mitad desde cero y al estilo Unix redondeará a la mitad a par: explorebinary.com/… - Reverendo

@Apoc Se define como "redondear al vínculo (entero) más cercano a par" en IEEE754. Más cercano es cualquier número entero que esté a menos de 0.5 (en magnitud) de distancia. Si sucede que el número es exactamente a la mitad (un empate) y luego redondear a par. Sí, Windows no sigue las especificaciones IEEE. - Isaac

Vea perldoc / perlfaq:

Recuerda que int() simplemente se trunca hacia 0. Para redondear a un cierto número de dígitos, sprintf() or printf() suele ser la ruta más fácil.

 printf("%.3f",3.1415926535);
 # prints 3.142

La POSIX módulo (parte de la distribución estándar de Perl) implementa ceil(), floor()y 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, se realizó trigonometría 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 utiliza 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 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 utilice Perl, sino implementar la función de redondeo que necesita usted mismo.

Para ver por qué, observe cómo seguirá teniendo 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 números enteros por debajo de 2 ** 31 (en máquinas de 32 bits) funcionarán de manera muy similar a los números enteros matemáticos. No se garantizan otros números.

Respondido el 14 de junio de 16 a las 01:06

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 hace esto es recorrer cada número positivo en el elemento, imprimir el número y el entero redondeado en el formato que mencionaste. El código concatena el entero positivo redondeado respectivo solo en función de los decimales. int ($ _) básicamente redondear a la baja el número entonces ($-int ($)) captura los decimales. Si los decimales son (por definición) estrictamente menores que 0.5, redondea el número hacia abajo. Si no es así, redondee agregando 1.

Respondido el 13 de junio de 11 a las 21:06

Una vez más, ¿por qué responder una pregunta antigua con una respuesta complicada cuando algo como la respuesta de RET funciona igualmente bien? - Joel Berger

Esto realmente no es muy complicado, y la respuesta de RET implica un montón de matemáticas que a) teóricamente corre el riesgo de desbordarse, b) lleva más tiempo yc) introduce innecesariamente más imprecisión de fp en su valor final. Espera, ¿cuál es complicado de nuevo? ;) - cptstubing06

Lo siguiente redondeará los números positivos o negativos a una posición decimal determinada:

sub round ()
{
    my ($x, $pow10) = @_;
    my $a = 10 ** $pow10;

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}

Respondido el 12 de Septiembre de 14 a las 15:09

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 usar sprintf(), pero también falla. El tercer uso sprintf() exitosamente mientras que los dos últimos (cuarto y quinto) 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) puede ser reemplazado con int($value + 0.5) para eliminar la dependencia de POSIX.

Respondido el 14 de junio de 16 a las 01:06

Los números negativos pueden agregar algunas peculiaridades que las personas deben conocer.

printfLos enfoques de estilo nos dan números correctos, pero pueden resultar en algunas pantallas extrañas. Hemos descubierto que este método (en mi opinión, estúpidamente) pone en - firmar si debe o no debe o no. Por ejemplo, -0.01 redondeado a un decimal devuelve -0.0, en lugar de solo 0. Si va a hacer el printf enfoque de estilo, y sabes que no quieres decimales, usa %d y no %f (cuando necesita decimales, es cuando la pantalla se torna débil).

Si bien es correcto y para las matemáticas no es gran cosa, para la visualización se ve extraño 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 se pueden hacer que sean correctos).

La int + 0.5 causa problemas reales con números negativos, a menos que desee que funcione de esa manera, pero imagino que la mayoría de la gente no lo hace. -0.9 probablemente debería redondearse a -1, no a 0. Si sabe que desea que el negativo sea un techo en lugar de un piso, puede hacerlo en 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;

Respondido el 14 de junio de 16 a las 01:06

Mi solución para 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 );

Respondido 14 Abr '12, 17:04

Si solo le preocupa obtener un valor entero de un número de punto flotante completo (es decir, 12347.9999 o 54321.0001), este enfoque (tomado prestado y modificado desde arriba) hará el truco:

my $rounded = floor($float + 0.1); 

Respondido el 23 de Septiembre de 16 a las 20:09

Mucha documentación de lectura sobre cómo redondear números, 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 de muchos lugares decimales, no solo uno, dos o tres. con eso en mente, aquí está mi solución (aunque no EXACTAMENTE según 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);
}

Respondido el 07 de diciembre de 19 a las 11:12

para que la subrutina se ajuste a sus especificaciones, simplemente modifique lo siguiente: if ($digit3 >= 5) { $digit3 = 0; $digit2++; if ($digit2 > 9) { $digit2 = 0; $digit1++; if ($digit1 > 9) { $digit1 = 0; $cost[0]++; } } } entonces es: if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; } entonces solo return commafied($cost[0]); - jarett lloyd

cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 

Respondido 12 Feb 14, 09:02

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.