[java] 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;
}

This question is related to java floating-point currency

The answer is


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.


Floating point numbers only have so many significant digits, but they can go much higher. If your app will ever handle large numbers, you will notice the epsilon value should be different.

0.001+0.001 = 0.002 BUT 12,345,678,900,000,000,000,000+1=12,345,678,900,000,000,000,000 if you are using floating point and double. It's not a good representation of money, unless you are damn sure you'll never handle more than a million dollars in this system.


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.


While I agree with the idea that double is bad for money, still the idea of comparing doubles has interest. In particular the suggested use of epsilon is only suited to numbers in a specific range. Here is a more general use of an epsilon, relative to the ratio of the two numbers (test for 0 is omitted):

boolean equal(double d1, double d2) {
  double d = d1 / d2;
  return (Math.abs(d - 1.0) < 0.001);
}

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);
}

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


Cents? If you're calculationg money values you really shouldn't use float values. Money is actually countable values. The cents or pennys etc. could be considered the two (or whatever) least significant digits of an integer. You could store, and calculate money values as integers and divide by 100 (e.g. place dot or comma two before the two last digits). Using float's can lead to strange rounding errors...

Anyway, if your epsilon is supposed to define the accuracy, it looks a bit too small (too accurate)...


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


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.


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.


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);
}

Cents? If you're calculationg money values you really shouldn't use float values. Money is actually countable values. The cents or pennys etc. could be considered the two (or whatever) least significant digits of an integer. You could store, and calculate money values as integers and divide by 100 (e.g. place dot or comma two before the two last digits). Using float's can lead to strange rounding errors...

Anyway, if your epsilon is supposed to define the accuracy, it looks a bit too small (too accurate)...


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


Floating point numbers only have so many significant digits, but they can go much higher. If your app will ever handle large numbers, you will notice the epsilon value should be different.

0.001+0.001 = 0.002 BUT 12,345,678,900,000,000,000,000+1=12,345,678,900,000,000,000,000 if you are using floating point and double. It's not a good representation of money, unless you are damn sure you'll never handle more than a million dollars in this system.


Cents? If you're calculationg money values you really shouldn't use float values. Money is actually countable values. The cents or pennys etc. could be considered the two (or whatever) least significant digits of an integer. You could store, and calculate money values as integers and divide by 100 (e.g. place dot or comma two before the two last digits). Using float's can lead to strange rounding errors...

Anyway, if your epsilon is supposed to define the accuracy, it looks a bit too small (too accurate)...


Floating point numbers only have so many significant digits, but they can go much higher. If your app will ever handle large numbers, you will notice the epsilon value should be different.

0.001+0.001 = 0.002 BUT 12,345,678,900,000,000,000,000+1=12,345,678,900,000,000,000,000 if you are using floating point and double. It's not a good representation of money, unless you are damn sure you'll never handle more than a million dollars in this system.


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.


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


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.


As other commenters correctly noted, you should never use floating-point arithmetic when exact values are required, such as for monetary values. The main reason is indeed the rounding behaviour inherent in floating-points, but let's not forget that dealing with floating-points means also having to deal with infinite and NaN values.

As an illustration that your approach simply doesn't work, here is some simple test code. I simply add your EPSILON to 10.0 and look whether the result is equal to 10.0 -- which it shouldn't be, as the difference is clearly not less than EPSILON:

    double a = 10.0;
    double b = 10.0 + EPSILON;
    if (!equals(a, b)) {
        System.out.println("OK: " + a + " != " + b);
    } else {
        System.out.println("ERROR: " + a + " == " + b);
    }

Surprise:

    ERROR: 10.0 == 10.00001

The errors occurs because of the loss if significant bits on subtraction if two floating-point values have different exponents.

If you think of applying a more advanced "relative difference" approach as suggested by other commenters, you should read Bruce Dawson's excellent article Comparing Floating Point Numbers, 2012 Edition, which shows that this approach has similar shortcomings and that there is actually no fail-safe approximate floating-point comparison that works for all ranges of floating-point numbers.

To make things short: Abstain from doubles for monetary values, and use exact number representations such as BigDecimal. For the sake of efficiency, you could also use longs interpreted as "millis" (tenths of cents), as long as you reliably prevent over- and underflows. This yields a maximum representable values of 9'223'372'036'854'775.807, which should be enough for most real-world applications.


Floating point numbers only have so many significant digits, but they can go much higher. If your app will ever handle large numbers, you will notice the epsilon value should be different.

0.001+0.001 = 0.002 BUT 12,345,678,900,000,000,000,000+1=12,345,678,900,000,000,000,000 if you are using floating point and double. It's not a good representation of money, unless you are damn sure you'll never handle more than a million dollars in this system.


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.


As other commenters correctly noted, you should never use floating-point arithmetic when exact values are required, such as for monetary values. The main reason is indeed the rounding behaviour inherent in floating-points, but let's not forget that dealing with floating-points means also having to deal with infinite and NaN values.

As an illustration that your approach simply doesn't work, here is some simple test code. I simply add your EPSILON to 10.0 and look whether the result is equal to 10.0 -- which it shouldn't be, as the difference is clearly not less than EPSILON:

    double a = 10.0;
    double b = 10.0 + EPSILON;
    if (!equals(a, b)) {
        System.out.println("OK: " + a + " != " + b);
    } else {
        System.out.println("ERROR: " + a + " == " + b);
    }

Surprise:

    ERROR: 10.0 == 10.00001

The errors occurs because of the loss if significant bits on subtraction if two floating-point values have different exponents.

If you think of applying a more advanced "relative difference" approach as suggested by other commenters, you should read Bruce Dawson's excellent article Comparing Floating Point Numbers, 2012 Edition, which shows that this approach has similar shortcomings and that there is actually no fail-safe approximate floating-point comparison that works for all ranges of floating-point numbers.

To make things short: Abstain from doubles for monetary values, and use exact number representations such as BigDecimal. For the sake of efficiency, you could also use longs interpreted as "millis" (tenths of cents), as long as you reliably prevent over- and underflows. This yields a maximum representable values of 9'223'372'036'854'775.807, which should be enough for most real-world applications.


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.


Cents? If you're calculationg money values you really shouldn't use float values. Money is actually countable values. The cents or pennys etc. could be considered the two (or whatever) least significant digits of an integer. You could store, and calculate money values as integers and divide by 100 (e.g. place dot or comma two before the two last digits). Using float's can lead to strange rounding errors...

Anyway, if your epsilon is supposed to define the accuracy, it looks a bit too small (too accurate)...


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.


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.


While I agree with the idea that double is bad for money, still the idea of comparing doubles has interest. In particular the suggested use of epsilon is only suited to numbers in a specific range. Here is a more general use of an epsilon, relative to the ratio of the two numbers (test for 0 is omitted):

boolean equal(double d1, double d2) {
  double d = d1 / d2;
  return (Math.abs(d - 1.0) < 0.001);
}

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to floating-point

Convert list or numpy array of single element to float in python Convert float to string with precision & number of decimal digits specified? Float and double datatype in Java C convert floating point to int Convert String to Float in Swift How do I change data-type of pandas data frame to string with a defined format? How to check if a float value is a whole number Convert floats to ints in Pandas? Converting Float to Dollars and Cents Format / Suppress Scientific Notation from Python Pandas Aggregation Results

Examples related to currency

Converting Float to Dollars and Cents Best data type to store money values in MySQL How to use GOOGLEFINANCE(("CURRENCY:EURAUD")) function What data type to use for money in Java? Cast a Double Variable to Decimal Yahoo Finance All Currencies quote API Documentation How can I correctly format currency using jquery? Currency format for display Print Currency Number Format in PHP Why not use Double or Float to represent currency?