Java double comparison epsilon
I wrote a class that tests for equality, less than, and greater than with two doubles in Java. My general case is comparing price that can have an accuracy of a half cent. 59.005 compared to 59.395. Is the epsilon I chose adequate for those cases?
private final static double EPSILON = 0.00001;
/**
* Returns true if two doubles are considered equal. Tests if the absolute
* difference between two doubles has a difference less then .00001. This
* should be fine when comparing prices, because prices have a precision of
* .001.
*
* @param a double to compare.
* @param b double to compare.
* @return true true if two doubles are considered equal.
*/
public static boolean equals(double a, double b){
return a == b ? true : Math.abs(a - b) < EPSILON;
}
/**
* Returns true if two doubles are considered equal. Tests if the absolute
* difference between the two doubles has a difference less then a given
* double (epsilon). Determining the given epsilon is highly dependant on the
* precision of the doubles that are being compared.
*
* @param a double to compare.
* @param b double to compare
* @param epsilon double which is compared to the absolute difference of two
* doubles to determine if they are equal.
* @return true if a is considered equal to b.
*/
public static boolean equals(double a, double b, double epsilon){
return a == b ? true : Math.abs(a - b) < epsilon;
}
/**
* Returns true if the first double is considered greater than the second
* double. Test if the difference of first minus second is greater then
* .00001. This should be fine when comparing prices, because prices have a
* precision of .001.
*
* @param a first double
* @param b second double
* @return true if the first double is considered greater than the second
* double
*/
public static boolean greaterThan(double a, double b){
return greaterThan(a, b, EPSILON);
}
/**
* Returns true if the first double is considered greater than the second
* double. Test if the difference of first minus second is greater then
* a given double (epsilon). Determining the given epsilon is highly
* dependant on the precision of the doubles that are being compared.
*
* @param a first double
* @param b second double
* @return true if the first double is considered greater than the second
* double
*/
public static boolean greaterThan(double a, double b, double epsilon){
return a - b > epsilon;
}
/**
* Returns true if the first double is considered less than the second
* double. Test if the difference of second minus first is greater then
* .00001. This should be fine when comparing prices, because prices have a
* precision of .001.
*
* @param a first double
* @param b second double
* @return true if the first double is considered less than the second
* double
*/
public static boolean lessThan(double a, double b){
return lessThan(a, b, EPSILON);
}
/**
* Returns true if the first double is considered less than the second
* double. Test if the difference of second minus first is greater then
* a given double (epsilon). Determining the given epsilon is highly
* dependant on the precision of the doubles that are being compared.
*
* @param a first double
* @param b second double
* @return true if the first double is considered less than the second
* double
*/
public static boolean lessThan(double a, double b, double epsilon){
return b - a > epsilon;
}
Solution 1:
You do NOT use double to represent money. Not ever. Use java.math.BigDecimal
instead.
Then you can specify how exactly to do rounding (which is sometimes dictated by law in financial applications!) and don't have to do stupid hacks like this epsilon thing.
Seriously, using floating point types to represent money is extremely unprofessional.
Solution 2:
Yes. Java doubles will hold their precision better than your given epsilon of 0.00001.
Any rounding error that occurs due to the storage of floating point values will occur smaller than 0.00001. I regularly use 1E-6
or 0.000001 for a double epsilon in Java with no trouble.
On a related note, I like the format of epsilon = 1E-5;
because I feel it is more readable (1E-5 in Java = 1 x 10^-5). 1E-6 is easy to distinguish from 1E-5 when reading code whereas 0.00001 and 0.000001 look so similar when glancing at code I think they are the same value.
Solution 3:
Whoa whoa whoa. Is there a specific reason you're using floating-point for currency, or would things be better off with an arbitrary-precision, fixed-point number format? I have no idea what the specific problem that you're trying to solve is, but you should think about whether or not half a cent is really something you want to work with, or if it's just an artifact of using an imprecise number format.
Solution 4:
If you are dealing with money I suggest checking the Money design pattern (originally from Martin Fowler's book on enterprise architectural design).
I suggest reading this link for the motivation: http://wiki.moredesignpatterns.com/space/Value+Object+Motivation+v2
Solution 5:
If you can use BigDecimal, then use it, else:
/**
*@param precision number of decimal digits
*/
public static boolean areEqualDouble(double a, double b, int precision) {
return Math.abs(a - b) <= Math.pow(10, -precision);
}