Friday, March 1, 2013

When x does not equal x

When is x != x ? How can this be?

Take this:


> insert into bar (id, avalue) values (22, null);
Query returned successfully: 1 rows affected, 18 ms execution time.
> select * from bar where avalue = null;
>


Hmm, but select * shows me that the row is there. OK, what about:


> select * from bar where not avalue = null;
>


I saw a bug only this week where somebody thought that just because a comparison with null didn't return anything that noting the predicate would mean the the row will show up in the result set. This is not the case. You would have to use is null in SQL.

Comparisons with null in SQL do not compute. This is part of the SQL standard called F571 and all databases I cared to check implement it. You can't even join tables on null values. For instance:


> insert into foo (id, avalue) values (33, null);
> select * from foo f, bar b where f.avalue = b.avalue;
>


SQL and Java are different in the way that they treat nulls. SQL uses Three Value Logic where null means undefined (or unknown or whatever semantics the developer wants to associate it with).

But you might be surprised to know that Java too has a notion of x != x.

Of course,

assertTrue(null == null);

is true. However, with floating points, this:

assertTrue(aDouble == aDouble);

may not always be true. This is nothing to do with rounding errors. If aDouble = Double.NaN then this assertion will fail (where NaN is not a number). The same is true for Float.NaN as defined in the IEEE spec.

Interestingly, infinities can be compared.

assertTrue((10d/0) == Double.POSITIVE_INFINITY);
assertTrue(Double.POSITIVE_INFINITY == Double.POSITIVE_INFINITY);

passes their assertions. And although (10d/0) may return an infinity, (0d/0d) returns NaN. This is consistent with the rules of arithmetic as mathematicians rather than software engineers understand it.

Finally, note that autoboxing may change your results, thus:


    public void testNans() {
        checkDoublePrimitivesEqual(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
        checkDoubleObjectsNotEqual(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
    }

    private void checkDoubleObjectsNotEqual(Double d1, Double d2) {
        assertFalse(d1 == d2);
    }

    private static void checkDoublePrimitivesEqual(double d1, double d2) {
        assertTrue(d1 == d2);
    }


[1] Wikipedia.

No comments:

Post a Comment