Note that Squeak has reduced the number of collisions for floats by not erasing the most significant bits of each word.
I've tested the squeak implementation with Andres Valloud tool in VW, it does not perform so bad for the sets I tested in term of collisions...
Float>>hash
"Hash is reimplemented because = is implemented. Both words of the float are used. (The bitShift:'s ensure that the intermediate results do not become a large integer.) Care is taken to answer same hash as an equal Integer."
(self isFinite and: [self fractionPart = 0.0]) ifTrue: [^self truncated hash].
^ ((self basicAt: 1) bitShift: -4) +
((self basicAt: 2) bitShift: -4)
I also changed Fraction to use a hashMultiply and made a more explicit isAnExactFloat test.
Fraction>>hash
"Hash is reimplemented because = is implemented."
"Care is taken that a Fraction equal to a Float also has an equal hash"
self isAnExactFloat ifTrue: [^self asExactFloat hash].
"Else, I cannot be exactly equal to a Float, use own hash algorithm."
^numerator hash hashMultiply bitXor: denominator hash
Fraction>>isAnExactFloat
"Answer true if this Fraction can be converted exactly to a Float"
^ denominator isPowerOfTwo
and: ["I have a reasonable significand: not too big"
numerator highBitOfMagnitude <= Float precision
and: ["I have a reasonable exponent: not too small"
Float emin + denominator highBitOfMagnitude <= Float precision]]
Fraction>>asExactFloat
"When we know that this Fraction is an exact Float, this conversion is much faster than asFloat."
^numerator asFloat timesTwoPower: 1 - denominator highBit
Not that Fraction should allways be reduced (this is an invariant of the class, I hope well defined in class comment).
Integer hash is not that good because the risk of occupying consecutive buckets is high... I wonder if returning ^self hashMultiply would not arrange things here and reduce the requirements for spreading hashMultiply everywhere else?