estoy usando este fragmento de código en las pruebas unitarias para comparar si el resultado de 2 cálculos diferentes son los mismos, salvo errores matemáticos de punto flotante.
Funciona al observar la representación binaria del número de punto flotante. La mayor parte de la complicación se debe al hecho de que el signo de los números de coma flotante no es complemento de dos. Después de compensar eso, básicamente se reduce a una simple resta para obtener la diferencia en ULP (explicado en el comentario a continuación).
/**
* Compare two floating points for equality within a margin of error.
*
* This can be used to compensate for inequality caused by accumulated
* floating point math errors.
*
* The error margin is specified in ULPs (units of least precision).
* A one-ULP difference means there are no representable floats in between.
* E.g. 0f and 1.4e-45f are one ULP apart. So are -6.1340704f and -6.13407f.
* Depending on the number of calculations involved, typically a margin of
* 1-5 ULPs should be enough.
*
* @param expected The expected value.
* @param actual The actual value.
* @param maxUlps The maximum difference in ULPs.
*/
public static void compareFloatEquals(float expected, float actual, int maxUlps) {
int expectedBits = Float.floatToIntBits(expected) < 0 ? 0x80000000 - Float.floatToIntBits(expected) : Float.floatToIntBits(expected);
int actualBits = Float.floatToIntBits(actual) < 0 ? 0x80000000 - Float.floatToIntBits(actual) : Float.floatToIntBits(actual);
int difference = expectedBits > actualBits ? expectedBits - actualBits : actualBits - expectedBits;
return !Float.isNaN(expected) && !Float.isNaN(actual) && difference <= maxUlps;
}
Aquí es una versión para double
precisión flota:
/**
* Compare two double precision floats for equality within a margin of error.
*
* @param expected The expected value.
* @param actual The actual value.
* @param maxUlps The maximum difference in ULPs.
* @see Utils#compareFloatEquals(float, float, int)
*/
public static void compareDoubleEquals(double expected, double actual, long maxUlps) {
long expectedBits = Double.doubleToLongBits(expected) < 0 ? 0x8000000000000000L - Double.doubleToLongBits(expected) : Double.doubleToLongBits(expected);
long actualBits = Double.doubleToLongBits(actual) < 0 ? 0x8000000000000000L - Double.doubleToLongBits(actual) : Double.doubleToLongBits(actual);
long difference = expectedBits > actualBits ? expectedBits - actualBits : actualBits - expectedBits;
return !Double.isNaN(expected) && !Double.isNaN(actual) && difference <= maxUlps;
}
Declarar flotantes como: 'float a = 1.2f;' y dobles como 'double d = 1.2d;' También en su instrucción if: 'if (c == 3.6f)' –
Como adición a @bobah ' s respuesta, recomiendo mirar la función 'Math.ulp()'. – aeracode