To compare float and double values correctly in Java, we check whether the difference between them is smaller than a tolerance that fits the size of the numbers, instead of testing them with ==, because most decimal fractions such as 0.1 have no exact binary form. For exact decimal values such as money, we use BigDecimal and its compareTo() method.
We need such comparisons when we check computed results in tests, decide whether a measurement reached a target, detect a change in a sensor value, or match totals from two systems. The following example adds a 0.1 km lap eleven times in a running app and compares the total with 1.1 km.
double distance = 0.0;
for (int lap = 0; lap < 11; lap++) {
distance += 0.1;
}
double target = 1.1;
boolean equalOperator = distance == target; // false, distance is 1.0999999999999999
boolean withinTolerance = Math.abs(distance - target) < 1e-9; // true
int order = Double.compare(distance, target); // -1, distance is slightly smaller
Notice that the loop and the literal produce two different double values, so == says false although both mean 1.1 km. We see why the error appears, how float and double differ, how to choose a tolerance, what Double.compare() does with NaN and -0.0, and when BigDecimal is the better tool.
1. Why == Fails for Floating-Point Numbers
Java stores float and double values in the IEEE 754 binary format. A binary fraction can hold halves, quarters and eighths without error, but not tenths, in the same way that a decimal fraction cannot write 1/3 with a finite number of digits. The literal 0.1 is stored as the nearest binary value, which is a tiny bit more than 0.1.
BigDecimal storedTenth = new BigDecimal(0.1); // 0.1000000000000000055511151231257827021181583404541015625
double sum = 0.1 + 0.2; // 0.30000000000000004
boolean sumIsExact = sum == 0.3; // false
boolean quarterIsExact = 0.25 + 0.5 == 0.75; // true, quarters are exact in binary
Each arithmetic operation rounds its result to the nearest double, so small errors from the stored values and from rounding add up. Two calculations that are equal on paper can end in neighboring double values, and == compares the stored bits, not the intended decimal number.

2. Comparing a float With a double
A float has 24 bits of precision, about 7 decimal digits, whereas a double has 53 bits, about 15 to 16 digits. When we compare a float with a double, Java widens the float to double first, following the numeric promotion rules. Widening keeps the less precise value, so it does not turn 0.1f into 0.1.
float priceFloat = 0.1f;
double priceDouble = 0.1;
double widened = priceFloat; // 0.10000000149011612
boolean mixed = priceFloat == priceDouble; // false
boolean asFloats = priceFloat == (float) priceDouble; // true, both rounded to float
boolean exactValue = 5.25f == 5.25; // true, 5.25 is exact in binary
When one value comes from a float source, such as a sensor library or a graphics API, we compare at float precision by casting the double to float, or we use a tolerance of about Math.ulp(value) of the float. Comparing the widened float with a double almost always fails for numbers with decimal fractions.
3. Comparing With a Tolerance (Epsilon)
A tolerance, often called epsilon, is the largest difference we still accept as equal. The check Math.abs(a – b) < epsilon works well when we know the size of the numbers, for example kilometers between 0 and 100 with an epsilon of 1e-9.
A fixed absolute epsilon breaks for large and for tiny numbers. Near 12 million, two neighboring double values are about 2e-9 apart, so 1e-9 is stricter than the precision of the type. Near 1e-10, the same epsilon is larger than the numbers themselves and calls 1e-10 and 3e-10 equal.
double total = 4_000_000.7 * 3; // 1.2000002100000001E7
boolean bigAbsolute = Math.abs(total - 12_000_002.1) < 1e-9; // false, the difference is 1.86E-9
boolean tinyAbsolute = Math.abs(1e-10 - 3e-10) < 1e-9; // true, although one value is three times the other
double gapNearOne = Math.ulp(1.1); // 2.220446049250313E-16
double gapNearBig = Math.ulp(1e15); // 0.125
The method Math.ulp() returns the distance from a value to the next larger double, which shows how the precision drops as numbers grow. A relative tolerance scales with the numbers, and a small absolute tolerance handles values near zero, so a general helper uses both.
static boolean nearlyEqual(double a, double b, double relTol, double absTol) {
if (a == b) {
return true; // same value, also equal infinities
}
double diff = Math.abs(a - b);
return diff <= absTol || diff <= relTol * Math.max(Math.abs(a), Math.abs(b));
}
boolean small = nearlyEqual(0.1 + 0.2, 0.3, 1e-12, 1e-12); // true
boolean big = nearlyEqual(4_000_000.7 * 3, 12_000_002.1, 1e-12, 1e-12); // true
boolean tiny = nearlyEqual(1e-10, 3e-10, 1e-12, 1e-12); // false
boolean nan = nearlyEqual(Double.NaN, Double.NaN, 1e-12, 1e-12); // false
A relative tolerance of 1e-12 leaves about four digits of room for rounding errors in a double, and 1e-5 leaves about two digits of room in a float. The absolute tolerance depends on the domain, such as half a cent for prices or a millimeter for distances. Libraries offer the same check, for example Guava DoubleMath.fuzzyEquals() and Apache Commons Math Precision.equals().
Unit tests use the same idea. JUnit’s assertEquals(double expected, double actual, double delta) passes when the difference is at most delta, so a test of a computed sum looks like this.
Assertions.assertEquals(0.3, 0.1 + 0.2, 1e-9); // passes, the difference is 5.6E-17
4. Double.compare(), equals(), NaN and -0.0
The Double.compare() method orders two values numerically, not by approximate equality, so it gives the same answer as == for ordinary numbers. It differs from == only in the two special cases where == breaks the rules that sorting and hash-based collections need.
double nan = 0.0 / 0.0;
boolean nanEquals = nan == nan; // false, NaN is never == to anything
int nanCompare = Double.compare(nan, nan); // 0
boolean nanCheck = Double.isNaN(nan); // true
boolean zeros = 0.0 == -0.0; // true
int zeroCompare = Double.compare(0.0, -0.0); // 1, 0.0 sorts after -0.0
boolean boxedZeros = Double.valueOf(0.0).equals(-0.0); // false
The rules differ per method, which is easiest to see side by side.
| Comparison | NaN vs NaN | 0.0 vs -0.0 | Use it for |
|---|---|---|---|
| a == b | false | equal | Exact checks on values known to be exact, such as 0.5 |
| Double.compare(a, b) | 0 (equal) | 1 (different) | Sorting and comparators |
| Double.valueOf(a).equals(b) | true | false | HashMap keys and List.contains() |
| nearlyEqual(a, b, rel, abs) | false | equal | Computed results |
A comparator built on a tolerance is not transitive, so it must never be used for sorting. With an epsilon of 0.1, the values 1.0 and 1.08 are equal and 1.08 and 1.16 are equal, but 1.0 and 1.16 are not, which breaks the Comparator contract. We sort with Double.compare() or Comparator.comparingDouble() and apply the tolerance only when we test results.
5. Comparing Exact Decimals With BigDecimal
For money, quantities with a fixed number of decimals and any value that must match a printed receipt, a tolerance is the wrong tool. The BigDecimal class stores a decimal number as an unscaled integer and a scale, without rounding it to binary, so 19.99 stays 19.99. Say an invoice service adds up its line items and compares the result with the total that the payment provider sends back.
BigDecimal first = new BigDecimal("19.99");
BigDecimal second = new BigDecimal("5.01");
BigDecimal invoiceTotal = first.add(second); // 25.00
BigDecimal paidTotal = new BigDecimal("25");
boolean sameAmount = invoiceTotal.compareTo(paidTotal) == 0; // true
boolean sameObject = invoiceTotal.equals(paidTotal); // false, scale 2 vs scale 0
The method compareTo() compares only the numeric value, whereas equals() also compares the scale, so 25.00 and 25 are not equal under equals(). That difference matters in a HashSet of amounts too, which can hold both.
How we create the BigDecimal decides whether it is exact. The String constructor and BigDecimal.valueOf(double) give the decimal value we expect, whereas the BigDecimal(double) constructor copies the binary error of the double.
BigDecimal fromConstructor = new BigDecimal(0.1); // 0.1000000000000000055511151231257827021181583404541015625
BigDecimal fromValueOf = BigDecimal.valueOf(0.1); // 0.1
BigDecimal fromSum = BigDecimal.valueOf(0.1 + 0.2); // 0.30000000000000004, the error is already in the double
BigDecimal rounded = fromSum.setScale(2, RoundingMode.HALF_UP); // 0.30
Converting a computed double to BigDecimal does not remove an error that already happened. Rounding to a fixed number of places with setScale() before comparing is a valid tolerance check when the business rule is about decimal places, such as two places for a displayed price.
6. Float and Double Comparison FAQs
Equality, ordering and money are three different problems, and the questions about floating-point comparison mix all of them.
6.1. Can we compare a float and a double with ==?
The comparison compiles, but it gives true only when the float holds a value that is exact in binary, such as 5.25. For values such as 0.1f and 0.1, we cast the double to float or use a tolerance, as in section 2.
6.2. What epsilon value should we use?
There is no single value. For a relative tolerance, 1e-12 fits most double calculations and 1e-5 fits float, and for an absolute tolerance we pick the smallest difference that matters in the domain, such as 0.005 for prices in dollars.
6.3. Is Double.compare() safer than ==?
The method is safer for sorting, because it treats NaN as equal to itself and orders -0.0 before 0.0. It does not help with rounding errors, because Double.compare(0.1 + 0.2, 0.3) returns 1, not 0.
6.4. Should we use double or BigDecimal for money?
We use BigDecimal, or a long number of cents. A double cannot store most cent amounts without error, so sums drift and comparisons with == fail on values that a customer sees as equal.
6.5. Why does 0.1 + 0.2 print 0.30000000000000004 in Java?
Both literals are stored as the nearest binary values, and their sum rounds to the double right above 0.3. The method Double.toString() prints the shortest decimal that identifies that double, which is 0.30000000000000004.
7. Conclusion
The == operator compares stored binary values, so it fails for most decimal fractions after any arithmetic. For computed float and double results, we compare with a relative tolerance plus a small absolute one, and we compare a float with a double at float precision.
For sorting and collections, Double.compare() and equals() give consistent results with NaN and -0.0. For money and other exact decimals, BigDecimal created from a String or valueOf() and compared with compareTo() gives the expected answer. The primitive data types guide covers the ranges of both types, and more small programs are in our Java examples.
8. References
- JLS 25, Numerical Equality Operators
- JLS 25, Floating-Point Types and Values
- Double Javadoc, Java 25
- Math.ulp() Javadoc
- BigDecimal Javadoc, Java 25
Happy Learning !!
This won’t work for infinity values. Here’s a test case to demonstrate this:
import java.util.*; import java.lang.*; import java.io.*; /* Name of the class has to be "Main" only if the class is public. */ class Ideone { public static void main (String[] args) throws java.lang.Exception { double a = Double.POSITIVE_INFINITY; double b = Double.POSITIVE_INFINITY; if(a == b) { System.out.println("'==' says a is equal to b"); } else { System.out.println("'==' says a is not equal to b"); } if(Math.abs(a - b) < 0.0001) { System.out.println("'THRESHOLD' based equality check says a is equal to b"); } else { System.out.println("'THRESHOLD' based equality check says a is not equal to b"); } } }public static boolean fuzzyEquals(double a, double b, double tolerance) {
MathPreconditions.checkNonNegative(“tolerance”, tolerance);
return Math.copySign(a – b, 1.0) <= tolerance
// copySign(x, 1.0) is a branch-free version of abs(x), but with different NaN semantics
|| (a == b) // needed to ensure that infinities equal themselves
|| (Double.isNaN(a) && Double.isNaN(b));
}
copy from guava DoubleMath
I tried this threshold value of floating number one but I am not getting both are same.
Please have a look and correct me.
package po; public class FloatExample { static double d1=0.0; static double d2=.1; static double thresholdValue = 0.0001; private static double doubleOne(){ for(int i=1; i<=11; i++){ d1=d1+.1; } return d1; } private static double doubleTwo(){ d2=d2*11; return d2; } public static void main(String[] args) { doubleOne(); doubleTwo(); if(Math.abs(doubleOne()-doubleTwo())<thresholdValue){ System.out.println("Both are equal"); }else{ System.out.println("Both are not equal"); } } }Output:
Both are not equal
Its because you are using global variables. Try something like below
private static double doubleOne(){ double d1=0.0; for(int i=1; i<=11; i++){ d1=d1+.1; } return d1; } private static double doubleTwo(){ double d2=.1; d2=d2*11; return d2; }Both methods are changing global var and are invoked twice. doubleOne results in 0.1*11+0.1*11, doubleTwo results in 0.1*11*11
You should:
Either don’t change global vars in the methods, that’s the best.
Invoke them once only, there is no need in invocation prior to if – remove those two method calls before the if block in main.