summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-11-12 22:13:04 +0000
committerPaul Phillips <paulp@improving.org>2009-11-12 22:13:04 +0000
commitb88e47ced9595d4193723c41e491bac318a59b80 (patch)
tree3006e49f4949633f131f05dd415875301c621b77 /src/library
parent07c295560c191297b3e4dae591878913b6461f74 (diff)
downloadscala-b88e47ced9595d4193723c41e491bac318a59b80.tar.gz
scala-b88e47ced9595d4193723c41e491bac318a59b80.tar.bz2
scala-b88e47ced9595d4193723c41e491bac318a59b80.zip
Bringing BigInt and BigDecimal into the club of...
Bringing BigInt and BigDecimal into the club of things which can be equal to one another and which will have the same hashCode. Fixed some old and some new bugs associated with equality. Note: not fully optimized.
Diffstat (limited to 'src/library')
-rw-r--r--src/library/scala/math/BigDecimal.scala12
-rw-r--r--src/library/scala/math/BigInt.scala15
-rw-r--r--src/library/scala/math/ScalaNumber.java1
-rw-r--r--src/library/scala/math/ScalaNumericConversions.scala27
-rw-r--r--src/library/scala/runtime/BoxesRunTime.java25
5 files changed, 64 insertions, 16 deletions
diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala
index c379b83abf..d5a57b2c62 100644
--- a/src/library/scala/math/BigDecimal.scala
+++ b/src/library/scala/math/BigDecimal.scala
@@ -165,17 +165,21 @@ extends ScalaNumber with ScalaNumericConversions
* which deems 2 == 2.00, whereas in java these are unequal
* with unequal hashCodes.
*/
- override def hashCode(): Int = doubleValue.hashCode()
+ override def hashCode(): Int =
+ if (isWhole) unifiedPrimitiveHashcode
+ else doubleValue.hashCode()
/** Compares this BigDecimal with the specified value for equality.
* Will only claim equality with scala.BigDecimal and java.math.BigDecimal.
*/
override def equals (that: Any): Boolean = that match {
- case that: BigDecimal => this equals that
- case that: BigDec => this equals BigDecimal(that)
- case _ => false
+ case that: BigDecimal => this equals that
+ case that: BigInt => this.toBigIntExact exists (that equals _)
+ case x => unifiedPrimitiveEquals(x)
}
+ override protected def isWhole = (this remainder 1) == BigDecimal(0)
+
/** Compares this BigDecimal with the specified BigDecimal for equality.
*/
def equals (that: BigDecimal): Boolean = compare(that) == 0
diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala
index f1e89f4f53..5e4bb569b5 100644
--- a/src/library/scala/math/BigInt.scala
+++ b/src/library/scala/math/BigInt.scala
@@ -24,6 +24,9 @@ object BigInt {
private val maxCached = 1024
private val cache = new Array[BigInt](maxCached - minCached + 1)
+ val MinLong = BigInt(Long.MinValue)
+ val MaxLong = BigInt(Long.MaxValue)
+
/** Constructs a <code>BigInt</code> whose value is equal to that of the
* specified integer value.
*
@@ -112,16 +115,20 @@ object BigInt {
class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericConversions
{
/** Returns the hash code for this BigInt. */
- override def hashCode(): Int = this.bigInteger.hashCode()
+ override def hashCode(): Int =
+ if (this >= BigInt.MinLong && this <= BigInt.MaxLong) unifiedPrimitiveHashcode
+ else bigInteger.hashCode
/** Compares this BigInt with the specified value for equality.
*/
override def equals(that: Any): Boolean = that match {
- case that: BigInt => this equals that
- case that: BigInteger => this equals new BigInt(that)
- case _ => false
+ case that: BigInt => this equals that
+ case that: BigDecimal => that.toBigIntExact exists (this equals _)
+ case x => unifiedPrimitiveEquals(x)
}
+ override protected def isWhole = true
+
/** Compares this BigInt with the specified BigInt for equality.
*/
def equals (that: BigInt): Boolean = compare(that) == 0
diff --git a/src/library/scala/math/ScalaNumber.java b/src/library/scala/math/ScalaNumber.java
index bb54a5d9c0..4ade1dee14 100644
--- a/src/library/scala/math/ScalaNumber.java
+++ b/src/library/scala/math/ScalaNumber.java
@@ -17,4 +17,5 @@ package scala.math;
* @since 2.8
*/
public abstract class ScalaNumber extends java.lang.Number {
+ protected abstract boolean isWhole();
}
diff --git a/src/library/scala/math/ScalaNumericConversions.scala b/src/library/scala/math/ScalaNumericConversions.scala
index 53465c7438..cc30d5d756 100644
--- a/src/library/scala/math/ScalaNumericConversions.scala
+++ b/src/library/scala/math/ScalaNumericConversions.scala
@@ -8,10 +8,12 @@
package scala.math
+import java.{ lang => jl }
+
/** Conversions which present a consistent conversion interface
* across all the numeric types.
*/
-trait ScalaNumericConversions extends java.lang.Number {
+trait ScalaNumericConversions extends ScalaNumber {
def toChar = intValue.toChar
def toByte = byteValue
def toShort = shortValue
@@ -19,4 +21,27 @@ trait ScalaNumericConversions extends java.lang.Number {
def toLong = longValue
def toFloat = floatValue
def toDouble = doubleValue
+
+ def isValidByte = isWhole && (toByte == toInt)
+ def isValidShort = isWhole && (toShort == toInt)
+ def isValidInt = isWhole && (toInt == toLong)
+ def isValidChar = isWhole && (toInt >= Char.MinValue && toInt <= Char.MaxValue)
+
+ protected def unifiedPrimitiveHashcode() = {
+ val lv = toLong
+ if (lv >= Int.MinValue && lv <= Int.MaxValue) lv.toInt
+ else lv.hashCode
+ }
+
+ protected def unifiedPrimitiveEquals(x: Any) = x match {
+ case x: Char => isValidChar && (toInt == x.toInt)
+ case x: Byte => isValidByte && (toByte == x)
+ case x: Short => isValidShort && (toShort == x)
+ case x: Int => isValidInt && (toInt == x)
+ case x: Long => toLong == x // XXX
+ case x: Float => toFloat == x // XXX
+ case x: Double => toDouble == x // XXX
+ case x: Number => this equals x
+ case _ => false
+ }
}
diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java
index 39ea9abcdd..869eb375ac 100644
--- a/src/library/scala/runtime/BoxesRunTime.java
+++ b/src/library/scala/runtime/BoxesRunTime.java
@@ -140,29 +140,37 @@ public class BoxesRunTime
/* COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON */
- // That's the method we should use from now on.
+ /** Since all applicable logic has to be present in the equals method of a ScalaNumber
+ * in any case, we dispatch to it as soon as we spot one on either side.
+ */
public static boolean equals(Object x, Object y) {
if (x instanceof Number) {
+ if (x instanceof ScalaNumber)
+ return x.equals(y);
+
Number xn = (Number)x;
if (y instanceof Number) {
- Number yn = (Number)y;
- if ((y instanceof ScalaNumber) && !(x instanceof ScalaNumber)) {
+ if (y instanceof ScalaNumber)
return y.equals(x);
- }
+
+ Number yn = (Number)y;
if ((xn instanceof Double) || (yn instanceof Double))
return xn.doubleValue() == yn.doubleValue();
if ((xn instanceof Float) || (yn instanceof Float))
return xn.floatValue() == yn.floatValue();
if ((xn instanceof Long) || (yn instanceof Long))
return xn.longValue() == yn.longValue();
- return xn.intValue() == yn.intValue();
+ if (typeCode(x) <= INT && typeCode(y) <= INT)
+ return xn.intValue() == yn.intValue();
+
+ return x.equals(y);
}
if (y instanceof Character)
return equalsNumChar(xn, (Character)y);
} else if (x instanceof Character) {
Character xc = (Character)x;
if (y instanceof Character)
- return (xc.charValue() == ((Character)y).charValue());
+ return xc.equals(y);
if (y instanceof Number)
return equalsNumChar((Number)y, xc);
} else if (x == null) {
@@ -181,7 +189,10 @@ public class BoxesRunTime
return x.longValue() == ch;
if (x instanceof ScalaNumber)
return x.equals(y);
- return x.intValue() == ch;
+ if (typeCode(x) <= INT)
+ return x.intValue() == ch;
+
+ return x.equals(y);
}
/** Hashcode algorithm is driven by the requirements imposed