El problema es que, como ha sugerido en su pregunta, está comparando una carroza con una doble.
Hay un problema más general al comparar flotadores, esto sucede porque cuando se hace un cálculo en un número de coma flotante, el resultado del cálculo puede no ser exactamente el esperado. Es bastante común que el último bit del flotador resultante sea incorrecto (aunque la inexactitud puede ser mayor que solo el último bit). Si usa ==
para comparar dos flotadores, entonces todos los bits tienen que ser iguales para que los flotadores sean iguales. Si su cálculo arroja un resultado ligeramente inexacto, entonces no se compararán cuando usted lo espera. En lugar de comparar los valores de esta manera, puede compararlos para ver si son casi iguales. Para hacer esto, puede tomar la diferencia positiva entre los flotadores y ver si es más pequeño que un valor dado (llamado épsilon).
Para elegir un buen épsilon necesita comprender un poco acerca de los números de coma flotante. Los números de coma flotante funcionan de manera similar a representar un número para un número dado de cifras significativas. Si trabajamos hasta 5 cifras significativas y su cálculo da como resultado que el último dígito del resultado sea incorrecto, entonces 1.2345 tendrá un error de + -0.0001, mientras que 1234500 tendrá un error de + -100. Si siempre basa su margen de error en el valor 1.2345, entonces su rutina de comparación será idéntica a ==
para todos los valores superiores a 10 (cuando se usa decimal). Esto es peor en binario, todos son valores mayores que 2. Esto significa que el épsilon que elegimos tiene que ser relativo al tamaño de los flotantes que estamos comparando.
FLT_EPSILON es la distancia entre 1 y el siguiente punto flotante más cercano. Esto significa que puede ser un buen épsilon elegir si su número está entre 1 y 2, pero si su valor es mayor que 2 usando este épsilon no tiene sentido porque la brecha entre 2 y el próximo float más cercano es mayor que épsilon. Entonces, tenemos que elegir un épsilon relativo al tamaño de nuestros flotadores (ya que el error en el cálculo es relativo al tamaño de nuestros flotadores).
Una buena (más o menos) de punto flotante comparar rutina se ve algo como esto:.
bool compareNearlyEqual (float a, float b, unsigned epsilonMultiplier)
{
float epsilon;
/* May as well do the easy check first. */
if (a == b)
return true;
if (a > b) {
epsilon = scalbnf(1.0f, ilogb(a)) * FLT_EPSILON * epsilonMultiplier;
} else {
epsilon = scalbnf(1.0, ilogb(b)) * FLT_EPSILON * epsilonMultiplier;
}
return fabs (a - b) <= epsilon;
}
Esta rutina de comparación compara los flotadores en relación con el tamaño de la más grande de flotación aprobada en scalbnf(1.0f, ilogb(a)) * FLT_EPSILON
encuentra la brecha entre a
y el siguiente flotador más cercano. Luego, se multiplica por epsilonMultiplier
, por lo que se puede ajustar el tamaño de la diferencia, dependiendo de cuán impreciso sea el resultado del cálculo.
Usted puede hacer un simple compareLessThan
rutina de este tipo:
bool compareLessThan (float a, float b, unsigned epsilonMultiplier)
{
if (compareNearlyEqual (a, b, epsilonMultiplier)
return false;
return a < b;
}
También podría escribir una función muy similar compareGreaterThan
.
Vale la pena señalar que la comparación de flotadores como este puede no ser siempre lo que usted desea. Por ejemplo, esto nunca encontrará que un float está cerca de 0 a menos que sea 0. Para solucionar esto, necesitaría decidir qué valor creía que era cercano a cero, y escribir una prueba adicional para esto.
En ocasiones, las imprecisiones que obtenga no dependerán del tamaño del resultado de un cálculo, sino que dependerán de los valores que ponga en un cálculo.Por ejemplo, sin(1.0f + (float)(200 * M_PI))
dará un resultado mucho menos preciso que sin(1.0f)
(los resultados deben ser idénticos). En este caso, su rutina de comparación debería tener en cuenta el número que ingresó en el cálculo para conocer el margen de error de la respuesta.
Véase también, "Lo que todo informático debe saber sobre Floating Point-Aritmética:" http://docs.sun.com/source/ 806-3568/ncg_goldberg.html –
Para aquellos de ustedes (especialmente @alastair) que han trabajado para mejorar mi respuesta, no estoy seguro de que pueda mejorarse. Estoy de acuerdo que fue incorrecto y probablemente peligroso. Lo he borrado Por favor, vea la respuesta de James Snook para una exploración mucho más profunda de este problema no trivial. –
@RobNapier Tengo que decir que creí que valía la pena mantener la respuesta, con las correcciones añadidas, pero comprendo su perspectiva al respecto. Disculpas si mi edición parecía un poco agresiva, solo quería aclarar cuál era el problema. – alastair