¿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 

Salida de perldoc -q round

¿Tiene Perl una función round ()? ¿Qué pasa con ceil () y piso ()? Trig funciones?

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

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

El módulo POSIX (parte de la distribución estándar de Perl) implementa ceil() , floor() y varias 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 módulo Math::Complex . Con 5.004, el módulo Math::Trig (parte de la distribución estándar de Perl) implementa las funciones trigonométricas. Internamente, utiliza el módulo Math::Complex y algunas funciones pueden dividirse desde el eje real en el 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 vale la pena no confiar en el redondeo de sistema que esté utilizando Perl, sino implementar la función de redondeo que necesita.

Para ver por qué, observe cómo todavía tendrá un problema en 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 por debajo de 2**31 (en máquinas de 32 bits) funcionarán más o menos como números enteros matemáticos. Otros números no están garantizados.

Si bien no está en desacuerdo con las respuestas complejas sobre marcas intermedias, etc., para el caso de uso más común (y posiblemente trivial):

my $rounded = int($float + 0.5);

ACTUALIZAR

Si es posible que $float 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 cero no explotará.

Puede usar un módulo como Math :: Round :

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

O puedes hacerlo de la manera más cruda:

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

Si decide usar printf o sprintf, tenga en cuenta que usan el método 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 

Ver perldoc / perlfaq :

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

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

El módulo POSIX (parte de la distribución estándar de Perl) implementa ceil() , floor() y varias 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 módulo Math::Complex .

Con 5.004, el módulo Math::Trig (parte de la distribución estándar de Perl)> implementa las funciones trigonométricas.

Internamente, utiliza el módulo Math::Complex y algunas funciones pueden dividirse desde el eje real en el 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 vale la pena no confiar en el redondeo de sistema que esté utilizando Perl, sino implementar la función de redondeo que necesita.

Para ver por qué, observe cómo todavía tendrá un problema en 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 por debajo de 2 ** 31 (en máquinas de 32 bits) funcionarán más o menos como números enteros matemáticos. Otros números no están garantizados.

No necesita ningún módulo externo.

 $x[0] = 1.2; $x[1] = 1.7; foreach (@x){ print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 ); print "\n"; } 

Me puede estar perdiendo su punto, pero pensé que esta era una manera mucho más limpia de hacer el mismo trabajo.

Lo que hace 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 redondea el número así que ($ -int ($ )) captura los decimales. Si los decimales son (por definición) estrictamente menores a 0.5, redondee el número. Si no, redondee agregando 1.

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

 sub round () { my ($x, $pow10) = @_; my $a = 10 ** $pow10; return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a); } 

A continuación se muestra una muestra de cinco maneras diferentes de sumr valores. La primera es una forma ingenua de realizar la sum (y falla). El segundo intenta usar sprintf() , pero también falla. El tercero usa sprintf() exitosamente mientras que los dos últimos (4to y 5to) 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 reemplazarse con int($value + 0.5) para eliminar la dependencia de POSIX .

Los números negativos pueden agregar algunas peculiaridades que la gente debe conocer.

printf enfoques tipo printf nos dan números correctos, pero pueden dar como resultado algunas visualizaciones impares. Hemos descubierto que este método (en mi opinión, estúpidamente) pone en un - signo si debe o no debería. Por ejemplo, -0.01 redondeado a un lugar decimal devuelve un -0.0, en lugar de solo 0. Si va a hacer el enfoque de estilo de printf , y sabe que no quiere ningún decimal, use %d y no %f (cuando lo necesite) decimales, es cuando la pantalla se pone gruesa).

Si bien es correcto y para las matemáticas no es gran cosa, para mostrarlo se ve raro mostrando algo así como “-0.0”.

Para el método int, los números negativos pueden cambiar lo que desee como resultado (aunque hay algunos argumentos que se pueden hacer son correctos).

El int + 0.5 causa problemas reales con 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 redondee a -1, no a 0. Si sabe que quiere que el negativo sea un techo en lugar de un piso, entonces puede hacerlo en un solo linaje, 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; 

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

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

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