perl floating-point rounding

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



floating-point rounding (12)

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

p.ej

1.2 = 1 1.7 = 2


A continuación se muestra una muestra de cinco maneras 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 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 .


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


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


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.


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;


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á.


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


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.


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


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