tags:

views:

193

answers:

1

I've recently encountered an odd situation when computing the hash Code of tuples of doubles in java. Suppose that you have the two tuples (1.0,1.0) and (Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY). Using the idiom stated in Joshua Bloch's Effective Java(Item 7), these two tuples would not be considered equal (Imagine that these tuples are objects). However, using the formula stated in Item 8 to compute hashCode() of each tuple evaluates to the same value.

So my question is: is there something strange about this formula that I missed out on when I was writing my formulas, or is it just an odd case of hash-code collisions?

Here is my short, comparative method to illustrate the situation (I wrote it as a JUnit4 test, but it should be pretty easily converted to a main method).

@Test
public void testDoubleHashCodeAndInfinity(){
    double a = 1.0;
    double b = 1.0;
    double c = Double.POSITIVE_INFINITY;
    double d = Double.POSITIVE_INFINITY;

    int prime = 31;
    int result1 = 17;
    int result2 = 17;

    long temp1 = Double.doubleToLongBits(a);
    long temp2 = Double.doubleToLongBits(c);
    //this assertion passes successfully
    assertTrue("Double.doubleToLongBits(Double.POSITIVE_INFINITY" +
            "==Double.doubleToLongBits(1.0)",temp1!=temp2);

    result1 = prime*result1 + (int)(temp1^(temp1>>>32));
    result2 = prime*result2 + (int)(temp2^(temp2>>>32));

    //this assertion passes successfully 
    assertTrue("Double.POSITIVE_INFINITY.hashCode()" +
            "==(1.0).hashCode()",result1!=result2);

    temp1 = Double.doubleToLongBits(b);
    temp2 = Double.doubleToLongBits(d);
    //this assertion should pass successfully
    assertTrue("Double.doubleToLongBits(Double.POSITIVE_INFINITY" +
            "==Double.doubleToLongBits(1.0)",temp1!=temp2);

    result1 = prime*result1+(int)(temp1^(temp1>>>32));
    result2 = prime*result2+(int)(temp2^(temp2>>>32));

    //this assertion fails!
    assertTrue("(1.0,1.0).hashCode()==" +
            "(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY).hashCode()",
            result1!=result2);
}
+4  A: 

It's just a coincidence. However, it's an interesting one. Try this:

Double d1 = 1.0;
Double d2 = Double.POSITIVE_INFINITY;

int hash1 = d1.hashCode();
int hash2 = d2.hashCode();

// These both print -1092616192
// This was me using the wrong hash combinator *and*
// the wrong tuples... but it's interesting
System.out.println(hash1 * 17 + hash2);
System.out.println(hash2 * 17 + hash1);

// These both print -33554432
System.out.println(hash1 * 31 + hash1);
System.out.println(hash2 * 31 + hash2);

Basically the bit patterns of the hash determine this. hash1 (1.0's hash code) is 0x3ff00000 and hash2 (infinity's hash code) is 0x7ff00000. That sort of hash and those sort of multipliers produces that sort of effect...

Executive summary: it's a coincidence, but don't worry about it :)

Jon Skeet