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

**true!**

*always*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().

## No comments:

## Post a Comment