Floating point: NaN, negative zero and equality
Values that are not equal to themselves, and why BigDecimal has two comparisons.
Open this lesson in the learning hubKey points
NaNis not equal to anything, including itself. Sox != xis a valid NaN test, and a NaN in a list makescontainsand sorting behave strangely.- Yet
Double.equalssays two NaNs are equal, because collections need a consistent equals. Two different notions of equality coexist deliberately. 0.0and-0.0compare equal with==but are distinguished byDouble.compareand byequals. That inconsistency breaks sorted-collection assumptions.BigDecimal.equalscompares scale as well as value, so2.0does not equal2.00. UsecompareTo() == 0for numeric equality - this is the single most common BigDecimal bug.- Because of that, a
HashSet<BigDecimal>can hold both 2.0 and 2.00 as distinct elements, which is almost never what anyone wants. - Never construct a BigDecimal from a
double.new BigDecimal(0.1)captures the full binary error; the String constructor orvalueOfgives the value you wrote.
Example
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
public class FloatingPointTraps {
public static void main(String[] args) {
double nan = 0.0 / 0.0;
System.out.println("nan == nan : " + (nan == nan));
System.out.println("Double.valueOf equals : "
+ Double.valueOf(nan).equals(Double.valueOf(nan)));
System.out.println("x != x is a NaN test : " + (nan != nan));
System.out.println();
System.out.println("0.0 == -0.0 : " + (0.0 == -0.0));
System.out.println("Double.compare(0,-0) : " + Double.compare(0.0, -0.0));
System.out.println("Double equals(0,-0) : "
+ Double.valueOf(0.0).equals(Double.valueOf(-0.0)));
System.out.println();
System.out.println("0.1 + 0.2 : " + (0.1 + 0.2));
System.out.println("== 0.3 : " + (0.1 + 0.2 == 0.3));
// BigDecimal: equals compares SCALE, compareTo compares VALUE.
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
System.out.println();
System.out.println("2.0.equals(2.00) : " + a.equals(b));
System.out.println("2.0.compareTo(2.00)==0: " + (a.compareTo(b) == 0));
Set<BigDecimal> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println("HashSet holds both : " + set.size() + " -> " + set);
// NEVER construct from a double.
System.out.println();
System.out.println("new BigDecimal(0.1) : " + new BigDecimal(0.1));
System.out.println("BigDecimal.valueOf : " + BigDecimal.valueOf(0.1));
System.out.println("new BigDecimal(\"0.1\") : " + new BigDecimal("0.1"));
}
}
NaN is not equal to itself, BigDecimal.equals compares scale not value, and a BigDecimal must never be built from a double.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Core Java course, and every lesson in it is listed on the Core Java contents page.