I see this Java bug time and time again:
NaN == NaN
So what's wrong with this?
The Float and Double classes in java.lang defines a constant holding a Not-a-Number (NaN) value of type float and double respectively. NaN can be used to represent a mathematically undefined number, such as that obtained by dividing zero by zero, or an unrepresentable value, such as the square root of a negative number, which is imaginary so cannot be represented as a real floating-point number. For instance:
System.out.println(0.0f / 0.0f);
System.out.println(Math.sqrt(-1.0f));
prints out:
NaN
NaN
Sometimes programmers initialize a class field to NaN to indicate that it has not been assigned a value. Later on in the program, they check if that field has been assigned a value by checking if it is equal to NaN, using the == operator, e.g.:
public class NaNTest {
private float value = Float.NaN;
public void setValue(float newValue) {
if (value == Float.NaN) // wrong, never do this!
value = newValue;
}
public float getValue() { return value; }
}
Unfortunately, value will never be set to newValue in the setValue() method, because (Float.NaN == Float.NaN) always returns false. In fact, if you look at the JDK implementation of Float.isNaN(), a number is not-a-number if it is not equal to itself (which makes sense because a number should be equal to itself). The same holds for Double.NaN.
This error is easy to make, because the == operator is what you will normally use to compare numbers and primitive types. This bug can go unnoticed for a long time, potentially giving disastrous consequences. For instance, if the code that uses the value returned by getValue() performs the same faulty equality check, and then performs some critical operations:
NaNTest test = new NaNTest();
test.setValue(4.0f); // does not set it 4.0f
float value = test.getValue(); // returns Float.NaN
float result = 0.0f;
if (value != Float.NaN) {
result = value;
}
System.out.println(result); // prints NaN
Although not immediately obvious, the printed value will always be NaN, not 4.0. This is because value has the value Float.NaN, and (Float.NaN != Float.NaN) is always true!
The correct way to check if a number is NaN is to use Float.isNaN() and Double.isNaN(). For example, continuing with the NaNTest class:
public void setValue(float newValue) {
if (Float.isNaN(value))
value = newValue;
}
Equivalently, this will also work:
public void setValue(float newValue) {
// works but don't do this
if (value != value) // yes, this check is weird!
value = newValue;
}
but you should use Float.isNaN() and Double.isNaN() because they make clear the intention of the check, and they will work regardless of any changes to the underlying floating-point implementation of Java.
Finally, the same applies to checking for positive and negative infinity: always use Float.isInfinite() and Double.isInfinite().