From 21bd081540413a8625247d2e40506112cc1ea218 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 9 Sep 2012 13:04:31 +0200 Subject: Improve Constant#hashCode - Incorporate `tag`, which is considered by equals, to reduce collisions. - Use the result of floatToRawIntBits(value) / doubleToRawLongBits(value), rather than value. This wasn't strictly necessary as (0d.## == (-0d).##) but this is more obviously correct. --- src/reflect/scala/reflect/internal/Constants.scala | 40 ++++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Constants.scala b/src/reflect/scala/reflect/internal/Constants.scala index 4d512e3864..b434be64a3 100644 --- a/src/reflect/scala/reflect/internal/Constants.scala +++ b/src/reflect/scala/reflect/internal/Constants.scala @@ -31,6 +31,9 @@ trait Constants extends api.Constants { final val EnumTag = 13 case class Constant(value: Any) extends ConstantApi { + import java.lang.Double.doubleToRawLongBits + import java.lang.Float.floatToRawIntBits + val tag: Int = value match { case null => NullTag case x: Unit => UnitTag @@ -81,18 +84,10 @@ trait Constants extends api.Constants { /** We need the equals method to take account of tags as well as values. */ + // !!! In what circumstance could `equalHashValue == that.equalHashValue && tag != that.tag` be true? override def equals(other: Any): Boolean = other match { case that: Constant => - // Consider two NaNs to be identical, despite non-equality - // Consider -0d to be distinct from 0d, despite equality - import java.lang.Double.doubleToRawLongBits - import java.lang.Float.floatToRawIntBits - - this.tag == that.tag && ((value, that.value) match { - case (f1: Float, f2: Float) => floatToRawIntBits(f1) == floatToRawIntBits(f2) - case (d1: Double, d2: Double) => doubleToRawLongBits(d1) == doubleToRawLongBits(d2) - case (v1, v2) => v1 == v2 - }) + this.tag == that.tag && equalHashValue == that.equalHashValue case _ => false } @@ -244,7 +239,30 @@ trait Constants extends api.Constants { def typeValue: Type = value.asInstanceOf[Type] def symbolValue: Symbol = value.asInstanceOf[Symbol] - override def hashCode: Int = value.## * 41 + 17 + /** + * Consider two `NaN`s to be identical, despite non-equality + * Consider -0d to be distinct from 0d, despite equality + * + * We use the raw versions (i.e. `floatToRawIntBits` rather than `floatToIntBits`) + * to avoid treating different encodings of `NaN` as the same constant. + * You probably can't express different `NaN` varieties as compile time + * constants in regular Scala code, but it is conceivable that you could + * conjure them with a macro. + */ + private def equalHashValue: Any = value match { + case f: Float => floatToRawIntBits(f) + case d: Double => doubleToRawLongBits(d) + case v => v + } + + override def hashCode: Int = { + import scala.util.hashing.MurmurHash3._ + val seed = 17 + var h = seed + h = mix(h, tag.##) // include tag in the hash, otherwise 0, 0d, 0L, 0f collide. + h = mix(h, equalHashValue.##) + finalizeHash(h, length = 2) + } } object Constant extends ConstantExtractor -- cgit v1.2.3