viernes, 29 de enero de 2010

Problema de precisión con el punto flotante en java (y en otros)

Estaba un día un amigo javero programando como cotidianamente lo hace, feliz en por la vida, cuando, de repente, al comprobar una igualdad en un algoritmo que parecía más que lógica se encontró con que algo no estaba funcionando como debiera.

En sí se trataba de una igualdad entre números de punto flotante, de tipo double  para ser más exacto (o doble si quieres usar una traducción literal, pero esto es programación, así que te quiero pedir que mejor no lo hagas, puede provocar confusiones). Como se trataba de una operación algo mas específica y seguramente compleja como para ponerla de ejemplo, mejor me remitiré al fragmento de código java que éste amigo me proporcionó para ejemplificar lo que pasa:
double test = 0.0;
for (int i = 0; i < 10; i++)
    test += 6111403.6;

//6111403.6 sumado 10 veces debería ser 61114036.0

//se agrega cero después del punto, así java lo toma como tipo double

if (test == 61114036.0)
    System.out.print("Igual");

else
    Syste.out.print("WTF!!??");

Consultando el valor de la variable 'test' al final nos da: ­test=6.111403600000001E7 en lugar de un varlor de 6.1114036E7 .... ¡así es, algo totalmente inesperado!

¿Pero qué es lo que sucede? bueno, veamos, los números de punto flotante (o coma flotante como le dicen en tierras del mediterráneo) se crearon con la intención de representar números reales. Un número de punto flotante se representa exactamente por la fórmula:
digitos significantes × baseexponente
y al parecer en nuestro problema algo tiene que ver la precisión que manejan los números de punto flotante  en su representación en sistemas informáticos (en la computadora).

Entonces la presición depende de que tantos valores se puedan almacenar de cada una de las partes de la fórumula de arriba, lo cual en ambientes informaticos, como todos saben, es finito, delimitado por el tipo de dato que se utilize para representar ese número real.

Entonces el hecho real es que los números de punto flotante no pueden representar absolutamente todos los números reales, entonces las operaciones que en ellos realizamos nos pueden llevar a situaciones sorpresivas como la que arriba ejemplificamos. Todo esto debido a la presición finita que las computadoras usan para representar estos números.


Para más información al respecto puedes consultar el articulo en la Wikipedia aqui.

Continuando con la historia, lo que mi amigo javero hizo para solucionar su problema, como le bastaba una presición con respecto a los primeros dos dígitos decimales, fue algo como lo siguiente con el valor en cuestión:
test = Math.round(test * 100) / 100;
de esta manera ya lo pudo comparar de manera confiable. Cada quien podrá implementar la manera que mejor le convenga, lo que si hay que conservar en mente, esque este tipo de problemas se pueden presentar en cualquier momento si estamos trabajando con valores de punto flotante, por lo tanto debemos tomar medidas si vemos que hay riesgo potencial de que ello signifique errores en nuestros sistemas.

Hay otras alternativas para evitar este problema, una explicación (que además esta en español pero no precisamente para java) se puede ver aqui.

Espero no haber confundido más con respecto a este tema y te sea de alguna utilidad, cualquier comentario aqui estamos...  ¡Paz!

No hay comentarios:

Publicar un comentario