AplusKminus
AplusKminus

Reputation: 1642

Comparing NaN in Kotlin

So I recently started loving the language kotlin. Today, while comparing doubles, I came across the inevitable NaN.

fun main(args: Array<String>) {
    val nan = Double.NaN
    println("1: " + (nan == nan))
    println("2: " + (nan == (nan as Number)))
    println("3: " + ((nan as Number) == nan))
}

N.B: (Double is a subtype of Number)

Running the above code yields:

1: false
2: true
3: true

I understand that comparing with NaN in Java returns false, so I would expect false for all expressions.

How can this behavior be explained? What is the rationale behind it?

Upvotes: 17

Views: 9348

Answers (2)

hotkey
hotkey

Reputation: 148149

That's because (2) and (3) are compiled to boxing a primitive and then Double.equals check: on JVM, primitive double can't be compared to a boxed one.

Double.equals, in turn, checks equality by comparing doubleToLongBits(...) of the two Doubles, and for the latter there's a guarantee that

If the argument is NaN, the result is 0x7ff8000000000000L.

So, the bits returned for two NaN are equal, and the rule NaN != NaN is ignored here.

Also, as @miensol mentioned, there's another consequence of this equality check: +0 and -0 are equal according to == check and not to equals check.

Equivalent code in Java would be:

double nan = Double.NaN;
System.out.println("1: " + (nan == nan)) //false 
System.out.println("2: " + ((Double) nan).equals(((Number) nan)))
System.out.println("3: " + ((Number) nan).equals(nan));

The last two lines call Double.equals, comparing doubleToLongBits(...).

Upvotes: 14

miensol
miensol

Reputation: 41678

The first comparison is equivalent to Java's:

double left = Double.NaN;
double right = Double.NaN;
boolean result = left == right;

And as you can read in this answer this is standarized and documented behavior.

The second & third comparison are equivalent to:

Double left = Double.valueOf(Double.NaN);
Number right = Double.valueOf(Double.NaN);
boolean result = left.equals(right);

Which uses Double.equals:

Note that in most cases, for two instances of class Double, d1 and d2, the value of d1.equals(d2) is true if and only if d1.doubleValue() == d2.doubleValue() also has the value true. However, there are two exceptions:

  • If d1 and d2 both represent Double.NaN, then the equals method returns true, even though Double.NaN==Double.NaN has the value false.

  • If d1 represents +0.0 while d2 represents -0.0, or vice versa, the equal test has the value false, even though +0.0==-0.0 has the value true.

Upvotes: 8

Related Questions