This is a follow up post for this question.There aren't a lot of changes. The changes (all of them major) are:I've decided to ditch my previous approach using Math.ulp() and adopt Tim Leek's suggestion: comparing ratios. In my updated code, I calculate both ratios and ensure that their difference from 1 is within the tolerance.The flexibility of the class is still kept in mind; getters and setters have been added to compliment the private static tolerance values.The constructor is made private to prevent instantiation (which I simply forgot about in the previous version).Basically, I fix my code when someone manages to break it. Tom Leek opened my eyes. Spotted's answer was nice too, but it's too bad that it must be done in a slightly different way to ensure compilation. It was a nice feeling when I saw my code pass in the cases mentioned by Tom Leek.package library.util;/** * This class provides a useful interface for easy (approximate) floating * point equality checks. This is necessary for several other classes. This * class provides the user the power to set custom values for the * <i>tolerances</i> used in comparison. * * @author Subhomoy Haldar (ambigram_maker) * @version 1.5 */public class Numbers {    /**     * Private constructor to prevent instantiation.     */    private Numbers() {        throw new AssertionError(No instances for you! ;P);    }    /**     * The default tolerance value for comparing the {@code float} values.     */    public static final float DEFAULT_FLOAT_TOLERANCE = 5E-8f;    /**     * The default tolerance value for comparing the {@code double} values.     */    public static final double DEFAULT_DOUBLE_TOLERANCE = 5E-16;    /**     * The current tolerance to be used for comparing floats.     */    private static float floatTolerance = DEFAULT_FLOAT_TOLERANCE;    /**     * The current tolerance to be used for comparing doubles.     */    private static double doubleTolerance = DEFAULT_DOUBLE_TOLERANCE;    /**     * Returns the current tolerance value for comparing {@code float}s.     *     * @return The current tolerance value for comparing {@code float}s.     */    public static float getFloatTolerance() {        return floatTolerance;    }    /**     * This method allows the user to set a custom value for the tolerance for     * comparing {@code float}s. The value must always be positive.     *     * @param floatTolerance The new tolerance value.     * @throws IllegalArgumentException If the value given is absurd.     */    public static void setFloatTolerance(float floatTolerance) {        if (floatTolerance <= 0) {            throw new IllegalArgumentException(Absurd tolerance value.);        }        Numbers.floatTolerance = floatTolerance;    }    /**     * Returns the current tolerance value for comparing {@code double}s.     *     * @return The current tolerance value for comparing {@code double}s.     */    public static double getDoubleTolerance() {        return doubleTolerance;    }    /**     * This method allows the user to set a custom value for the tolerance for     * comparing {@code double}s. The value must always be positive.     *     * @param doubleTolerance The new tolerance value.     * @throws IllegalArgumentException If the value given is absurd.     */    public static void setDoubleTolerance(double doubleTolerance) {        if (doubleTolerance <= 0) {            throw new IllegalArgumentException(Absurd tolerance value.);        }        Numbers.doubleTolerance = doubleTolerance;    }    /**     * Returns {@code true} if the arguments are <i>exactly</i> equal.     *     * @param bytes The arguments to check.     * @return {@code true} if the arguments are <i>exactly</i> equal.     */    public static boolean areEqual(byte... bytes) {        int length = bytes.length;        assertProperLength(length);        byte d = bytes[0];        for (int i = 1; i < length; i++) {            if (d != bytes[i]) {                return false;            }        }        return true;    }    /**     * Returns {@code true} if the arguments are <i>exactly</i> equal.     *     * @param shorts The arguments to check.     * @return {@code true} if the arguments are <i>exactly</i> equal.     */    public static boolean areEqual(short... shorts) {        int length = shorts.length;        assertProperLength(length);        short d = shorts[0];        for (int i = 1; i < length; i++) {            if (d != shorts[i]) {                return false;            }        }        return true;    }    /**     * Returns {@code true} if the arguments are <i>exactly</i> equal.     *     * @param ints The arguments to check.     * @return {@code true} if the arguments are <i>exactly</i> equal.     */    public static boolean areEqual(int... ints) {        int length = ints.length;        assertProperLength(length);        int d = ints[0];        for (int i = 1; i < length; i++) {            if (d != ints[i]) {                return false;            }        }        return true;    }    /**     * Returns {@code true} if the arguments are <i>exactly</i> equal.     *     * @param longs The arguments to check.     * @return {@code true} if the arguments are <i>exactly</i> equal.     */    public static boolean areEqual(long... longs) {        int length = longs.length;        assertProperLength(length);        long d = longs[0];        for (int i = 1; i < length; i++) {            if (d != longs[i]) {                return false;            }        }        return true;    }    /**     * Returns {@code true} if the arguments are <i>exactly</i> equal.     *     * @param chars The arguments to check.     * @return {@code true} if the arguments are <i>exactly</i> equal.     */    public static boolean areEqual(char... chars) {        int length = chars.length;        assertProperLength(length);        char d = chars[0];        for (int i = 1; i < length; i++) {            if (d != chars[i]) {                return false;            }        }        return true;    }    /**     * Returns {@code true} if the arguments are <i>approximately</i> equal.     *     * @param floats The arguments to check.     * @return {@code true} if the arguments are <i>approximately</i> equal.     * @see #areEqual(double, double) areEqual(double, double) -     * For an important note.     */    public static boolean areEqual(float... floats) {        int length = floats.length;        assertProperLength(length);        float d = floats[0];        for (int i = 1; i < length; i++) {            if (!areEqual(d, floats[i])) {                return false;            }        }        return true;    }    /**     * Returns {@code true} if the arguments are <i>approximately</i> equal.     * <p>     * <b>NOTE:</b> There are a few details which must be explained:     * <ol><li>When all the arguments are <i>infinities</i> (of the same sign),     * then this method returns {@code true}.</li>     * <li>When all the arguments are {@link Double#NaN Double.NaN}, then     * this method returns {@code true}.</li></ol>     * This goes <i>against</i> the standard specification (which requires that     * &infin; &ne; &infin; and {@code NaN} &ne; {@code NaN}). This is     * <i>counter-intuitive</i> and that is why this implementation does not     * follow the specification. The same applies for the     * {@link #areEqual(float, float) areEqual(float, float)} method.     *     * @param doubles The arguments to check.     * @return {@code true} if the arguments are <i>approximately</i> equal.     */    public static boolean areEqual(double... doubles) {        int length = doubles.length;        assertProperLength(length);        double d = doubles[0];        for (int i = 1; i < length; i++) {            if (!areEqual(d, doubles[i])) {                return false;            }        }        return true;    }    /**     * This method is used to ensure that the number of arguments is at least 2.     *     * @param length The number of arguments.     * @throws IllegalArgumentException If 1 or no arguments.     */    private static void assertProperLength(int length) throws            IllegalArgumentException {        if (length < 2) {            throw new IllegalArgumentException                    (At least two arguments required.);        }    }    private static boolean areEqual(double d1, double d2) {        // the corner cases first:        if (Double.isNaN(d1) && Double.isNaN(d2)) {            return true;        }        if (Double.isInfinite(d1) || Double.isInfinite(d2)) {            return d1 == d2;    // ensures same sign        }        if (d1 == 0 || d2 == 0) {            return Math.abs(d1 - d2) <= doubleTolerance;        }        // No more kludgy business with Math.ulp(). ;)        // Just compare the ratios:        double ratio1 = d1 / d2;    // Two ratios are there        double ratio2 = d2 / d1;    // for symmetry.        return d1 == d2 ||                Math.abs(ratio1 - 1) <= doubleTolerance &&                Math.abs(ratio2 - 1) <= doubleTolerance;    }    private static boolean areEqual(float f1, float f2) {        if (Float.isNaN(f1) && Float.isNaN(f2)) {            return true;        }        if (Float.isInfinite(f1) || Float.isInfinite(f2)) {            return f1 == f2;        }        if (f1 == 0 || f2 == 0) {            return Math.abs(f1 - f2) <= floatTolerance;        }        double ratio1 = f1 / f2;        double ratio2 = f2 / f1;        return f1 == f2 ||                Math.abs(ratio1 - 1) <= floatTolerance &&                Math.abs(ratio2 - 1) <= floatTolerance;    }}Now, for the cases mentioned by Tom Leek:double a = 4.6e4;   // any arbitrary valuedouble b = a + 0.9 * Numbers.getDoubleTolerance();double c = a - 0.9 * Numbers.getDoubleTolerance();assert Numbers.areEqual(a, b, c);assert Numbers.areEqual(b, a, c);assert !Numbers.areEqual(1e-30, 1e-36);double d = Math.pow(2, 53);double e = d + 1;assert d == e;assert Numbers.areEqual(d, e);The above code runs without resulting in any assertion errors. My request is simple: try breaking this one as well. ;-)Also, I would like the usual: suggestions on improving readability, reusability and so on.
Floating point equality in Java - follow-up
java;library;floating point