Excepciones de coma flotante y verificación de variables

Technorati Tags: , , ,

Cualquiera que haya programado cierto tiempo y haya tenido que realizar divisiones con variables que no se sabe a priori qué valor van a tomar sabe que, tarde o temprano, ocurrirá alguna excepción de coma flotante, debida a un intento de dividir por cero (porque la variable en ese momento tiene el valor cero, o uno muy cercano), terminándose la ejecución del programa.

Para evitar este problema, lo habitual es colocar una condición antes de realizar la operación de división y darle un valor fijo en caso de que el divisor sea cero. Algo tan sencillo como esto:

if (proporcion_[slot_out] > 0.0) {
deficit_[i] += (long long) (0.5 + (double) longitud_paquete * proporcion_[i] / proporcion_[slot_out]);
}

Debería ser suficiente para evitar que ocurriera algún problema ¿verdad? Pues no, ni mucho menos. Llevo unas semanas haciendo simulaciones con el código tal cual está. Hoy he cambiado a una red más grande y no hacía más que fallar, así que me he metido con el gdb y he encontrado que el fallo está en ese punto. Para comprobar que no entraba en ese ‘if’ he hecho lo que cualquiera: presentar por pantalla el valor de esa variable:

if (proporcion_[slot_out] > 0.0) {
printf(«Proporcion = %f\n», proporcion_[slot_out]);fflush(stdout);
// se puede hacer también sin redondear
deficit_[i] += (long long) (0.5 + (double) longitud_paquete * proporcion_[i] / proporcion_[slot_out]);
}

El resultado era el siguiente:

Proporcion = 0.000000
Exepción de coma flotante

Podríamos hablar del fallo horrográfico, pero no, a mí me gustaría saber por qué me presenta por pantalla el valor de la variable si se supone que al valer 0, no entra en el ‘if’. Arrea, que la precisión es finita, y los números que puede manejar la máquina pueden no ser representables con ese nivel de precisión. Hagamos un cambio en el código: que en lugar de mostrar con formato %f lo haga con formato %g (notación exponencial)…

if (proporcion_[slot_out] > 0.0) {
printf(«Proporcion = %g\n», proporcion_[slot_out]);fflush(stdout);
// se puede hacer también sin redondear
deficit_[i] += (long long) (0.5 + (double) longitud_paquete * proporcion_[i] / proporcion_[slot_out]);
}

El resultado, señoras y señores, no podía ser más preocupante:

Proporcion = 1.27e-319
Exepción de coma flotante

Teniendo en cuenta que si el valor de la proporción no es calculada en base a unos parámetros, su valor se fuerza a 0, es cuanto menos preocupante. Para arreglarlo nada como compararlo con un número muy pequeño, pero no demasiado, y eso porque trabajo con proporciones relativamente grandes (menores de 1, pero mayores de 1e-10, seguro), que si no, ya me iban a dar bien dado.

Share