• Publicidad

Generar un vector de valores

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Generar un vector de valores

Notapor ddiana » 2010-11-03 22:21 @973

Hola a todos, tengo un pequeño programa en Perl que debería generar números de 0.01 a 1 aumentando de 0.01 en 0.01. Funciona bien para valores de 0.01 a 0.81 pero después de este valor, en vez de generar 0.82 obtengo 0.820000000001 y no sé de dónde sale ese 0.0000000001 adicional. Pongo el código, es realmente muy sencillo pero no entiendo que está pasando.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$w=0;
for ($i=0;$i<=100;$i++)
   {
    print "omega--> $w \n";
    $w=$w+0.01;
   }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Y el resultado que obtengo es el siguiente (no pongo todos los valores):
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
omega--> 0
omega--> 0.01
omega--> 0.02
omega--> 0.03
omega--> 0.04

...

omega--> 0.81
omega--> 0.820000000000001
omega--> 0.830000000000001
omega--> 0.840000000000001
omega--> 0.850000000000001
omega--> 0.860000000000001
omega--> 0.870000000000001
omega--> 0.880000000000001
omega--> 0.890000000000001
omega--> 0.900000000000001
omega--> 0.910000000000001
omega--> 0.920000000000001
omega--> 0.930000000000001
omega--> 0.940000000000001
omega--> 0.950000000000001
omega--> 0.960000000000001
omega--> 0.970000000000001
omega--> 0.980000000000001
omega--> 0.990000000000001
omega--> 1
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Muchas gracias.

Diana
ddiana
Perlero nuevo
Perlero nuevo
 
Mensajes: 11
Registrado: 2010-02-08 13:26 @601

Publicidad

Re: Generar un vector de valores

Notapor wanako » 2010-11-04 01:13 @092

No hay nada malo en tu código, el punto flotante es el talón de Aquiles de las máquinas y compiladores. No es un problema exclusivo de Perl, en otros lenguajes el error aparece. Resolverlo es fácil:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use strict;
  2.  
  3. my $w = 0.0;
  4. use constant incremento => 0.01;
  5.  
  6. for ( 0 .. 100 ) {
  7.     printf "omega--> %.2f\n", $w;
  8.     $w += incremento;
  9. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Explicarlo es difícil, se lo dejo a explorer :D

perlfaq4: Why am I getting long decimals
What Every Computer Scientist Should Know About Floating-Point Arithmetic
wanako
Perlero nuevo
Perlero nuevo
 
Mensajes: 27
Registrado: 2010-09-23 11:27 @519

Re: Generar un vector de valores

Notapor explorer » 2010-11-04 07:36 @358

Sí, se trata de la falta de precisión que tienen los ordenadores, y su codificación en binario. Ya lo hemos hablado en otra ocasión.

En tu caso, cambiando el print() por printf(), lo solucionas fácil.

No importa qué lenguaje utilices, siempre hay que prestar atención cuando hacemos cuentas con números que tienen muchas cifras (delante o detrás de la coma decimal).

En Perl hay módulos especializados en el tratamiento de este tipo de números, como por ejemplo Math::BigInt.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Generar un vector de valores

Notapor salva » 2010-11-04 11:38 @526

ddiana escribiste:Hola a todos, tengo un pequeño programa en Perl que debería generar números de 0.01 a 1 aumentando de 0.01 en 0.01. Funciona bien para valores de 0.01 a 0.81 pero después de este valor, en vez de generar 0.82 obtengo 0.820000000001 y no sé de dónde sale ese 0.0000000001 adicional.

La representación que utilizan los ordenadores internamente para manejar números reales es, en general, una aproximación y por lo tanto hay que contar con que al hacer operaciones en punto flotante, estos errores pueden hacerse visibles.

No obstante, ¡eso no quita que podamos minimizar esos errores!

Por ejemplo, en tu caso estás sumando 0.01 (valor que, por cierto, el ordenador no es capaz de representar de manera exacta) a un contador dentro de un bucle. Cada operación introduce nuevos errores en el calculo, por lo que después de ochenta y pico iteraciones el error se hace visible. Al calcular de esa manera estas multiplicando el error inicial.

Una mejor forma de hacerlo es teniendo en cuenta que una suma repetida puede convertirse en una multiplicación:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$w0=0;
for ($i=0;$i<=100;$i++)
   {
    print "omega--> $w \n";
    $w=$w0 + $i * 0.01;
   }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


De esta manera el cálculo de $w se limita siempre a dos operaciones y no se produce la acumulación de errores de la que pecaba tu código.

Otra buena técnica es tratar de realizar las operaciones utilizando números enteros siempre que podamos. En tu caso:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$w0=0;
for ($i=0;$i<=100;$i++)
   {
    print "omega--> $w \n";
    $w=$w0 + $i / 100;
   }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Por último, en algunas ocasiones (aunque la verdad es que son casos raros), también te puede interesar utilizar alguno de los módulos que hay en CPAN para manejar números racionales como por ejemplo Math::BigRat. El módulo Math::BigInt que ya te ha recomendado explorer, también permite hacer operaciones en punto fijo (números racionales con un número predeterminado de decimales) que se emplean a menudo en cálculos financieros donde los redondeos importan mucho.
Avatar de Usuario
salva
Perlero nuevo
Perlero nuevo
 
Mensajes: 200
Registrado: 2008-01-03 15:19 @680


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 2 invitados

cron