Finalmente he podido entender el misterio del error de precisión de los números de punto flotante que afecta a todos los lenguajes de programación. Para explicarlo, voy a emular el trabajo que realiza el computador para almacenar y leer el número decimal 0.1
Como los computadores almacenan todos los números y caracteres en binario, el primer paso es transformar el número decimal 0.1
a su representación binaria. Para ello se sigue un algoritmo sencillo:
1) Se toma el número a transformar y se multiplica por 2
:
0.1 * 2 = 0.2
2) Se separa la parte entera de la decimal del valor resultante
La parte entera de 0.2 es 0
La parte decimal de 0.2 es 0.2
3) La parte entera se toma como el primer dígito significativo del número binario
0.0______
3) Se repiten los pasos 1 y 2 usando la parte decimal obtenida del paso anterior.
Este proceso se repite hasta que el resultado de sea cero o se obtenga una secuencia periódica.
Si ejecutamos este proceso completo para el número 0.1
obtenemos lo siguiente:
0.1 * 2 = 0.2 -> 0
0.2 * 2 = 0.4 -> 0
0.4 * 2 = 0.8 -> 0
0.8 * 2 = 1.6 -> 1
0.6 * 2 = 1.2 -> 1
0.2 * 2 = 0.4 -> 0 (vuelve a reiniciarse la secuencia)
Con esto obtenemos que la representación binaria de 0.1
es:
0.00011... periódico
Como las computadoras tienen una cantidad finita de bits para almacenar cualquier número, los números periódicos se almacenarán truncados. Si asumimos que nuestra computadora solo almacena el valor 0.00011
y no 0.000110011001100110011...
(hasta el infinito), la conversión de binario a decimal de este número sería:
0.00011 = 1/16 + 1/32 = 0.09375
Y con esto hemos comprobado cómo surge un problema de precisión pues 0.1
no es igual a 0.09375
, es solo un valor aproximado.
De acuerdo. Así es. Y en todo caso si el computador fuera decimal, la memoria de longitud fija finita hace perder precisión. Ejemplo: la expresión de la raiz de 2 es una serie infinita o tiene infinitos numeros en su parte decimal imposibles de registrar en un computador.
Por eso existen los métodos numéricos que tratan de minizar el efecto del control por redondeo y en muchos casos son métodos que en lugar de calcular directamente van produciendo aproximaciones a la respuesta cada vez mejores a medida que aumentan las iteraciones.
Yo llegué a descubrir este error de precisión trabajando con Javascript, en alguna parte del código tenía una comparación simple del tipo if(a==b) que fallaba inexplicablemente
Para evitar la posibilidad de error en comparaciones numéricas de igual, siempre se encontrará o es necesario algún truco. Ejemplo: En lugar de a=0, se compara a< 10^-7 o el numero conveniente que no supere la precisión del computador. Pero podría funcionar mal si por error o cálculo a no es positivo, en cuyo caso tendría que tener algo como 0<ab, en lugar de a=b, diría a-b<10^-7. Esos casos posibles requieren ingenio adicional. Cada caso puede ser diferente. Problemas podrían evitarse utilizando la función valor absoluto. En lugar de a=b estudiar abs(a-b)< 10^-7 por ejemplo
Muchas gracias!!
De nada 🙂
Así es. por eso los algoritmos numéricos se diseñan para controlar y corregir el error por redondeo, mas es inevitable en general. Se habla de estabilidad cuando el algoritmo controla al máximo el error por redondeo y condición del problema que significa que un problema mal condicionado podría reventar aun ante un “algoritmo” estable.