From b72cc0bda59df3b73afb128b24d86b0cbb1fbf43 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 11 Jun 2009 17:52:07 +0000 Subject: Lots and lots of BigInt and BigDecimal code att... Lots and lots of BigInt and BigDecimal code attempting to make it all consistent and equality correct and consistent (to the extent that's even possible.) I'll be glad when this is over. --- src/library/scala/BigDecimal.scala | 90 ++++++++++++++++++++++++++++---------- src/library/scala/BigInt.scala | 26 ++++++++--- 2 files changed, 87 insertions(+), 29 deletions(-) (limited to 'src/library') diff --git a/src/library/scala/BigDecimal.scala b/src/library/scala/BigDecimal.scala index 54409bfa32..27d1c19b26 100644 --- a/src/library/scala/BigDecimal.scala +++ b/src/library/scala/BigDecimal.scala @@ -37,13 +37,14 @@ object BigDecimal * @param i the specified integer value * @return the constructed BigDecimal */ - def apply(i: Int): BigDecimal = + def apply(i: Int): BigDecimal = apply(i, defaultMathContext) + def apply(i: Int, mc: MathContext): BigDecimal = if (minCached <= i && i <= maxCached) { val offset = i - minCached var n = cache(offset) - if (n eq null) { n = new BigDecimal(BigDec.valueOf(i)); cache(offset) = n } + if (n eq null) { n = new BigDecimal(BigDec.valueOf(i), mc); cache(offset) = n } n - } else new BigDecimal(BigDec.valueOf(i)) + } else new BigDecimal(BigDec.valueOf(i), mc) /** Constructs a BigDecimal whose value is equal to that of the * specified long value. @@ -53,11 +54,23 @@ object BigDecimal */ def apply(l: Long): BigDecimal = if (minCached <= l && l <= maxCached) apply(l.toInt) - else new BigDecimal(BigDec.valueOf(l)) + else new BigDecimal(BigDec.valueOf(l), defaultMathContext) + def apply(l: Long, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(l, mc), mc) + + /** Constructs a BigDecimal whose unscaled value is equal to that + * of the specified long value. + * + * @param unscaledVal the value + * @param scale the scale + * @return the constructed BigDecimal + */ def apply(unscaledVal: Long, scale: Int): BigDecimal = - if (scale == 0) apply(unscaledVal) - else new BigDecimal(BigDec.valueOf(unscaledVal, scale)) + apply(BigInt(unscaledVal), scale) + + def apply(unscaledVal: Long, scale: Int, mc: MathContext): BigDecimal = + apply(BigInt(unscaledVal), scale, mc) /** Constructs a BigDecimal whose value is equal to that of the * specified double value. @@ -65,21 +78,23 @@ object BigDecimal * @param d the specified Double value * @return the constructed BigDecimal */ - def apply(d: Double): BigDecimal = - new BigDecimal(BigDec.valueOf(d)) + def apply(d: Double): BigDecimal = apply(d, defaultMathContext) + // note we don't use the static valueOf because it doesn't let us supply + // a MathContext, but we should be duplicating its logic, modulo caching. + def apply(d: Double, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(java.lang.Double.toString(d), mc), mc) /** Translates a character array representation of a BigDecimal * into a BigDecimal. */ - def apply(x: Array[Char]): BigDecimal = - new BigDecimal(new BigDec(x.toString)) + def apply(x: Array[Char]): BigDecimal = apply(x, defaultMathContext) + def apply(x: Array[Char], mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(x.toString, mc), mc) /** Translates the decimal String representation of a BigDecimal * into a BigDecimal. */ - def apply(x: String): BigDecimal = - new BigDecimal(new BigDec(x)) - + def apply(x: String): BigDecimal = apply(x, defaultMathContext) def apply(x: String, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(x, mc), mc) @@ -89,8 +104,23 @@ object BigDecimal * @param x the specified BigInt value * @return the constructed BigDecimal */ - def apply(x: BigInt): BigDecimal = - new BigDecimal(new BigDec(x.bigInteger)) + def apply(x: BigInt): BigDecimal = apply(x, defaultMathContext) + def apply(x: BigInt, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(x.bigInteger, mc), mc) + + /** Constructs a BigDecimal whose unscaled value is equal to that + * of the specified BigInt value. + * + * @param unscaledVal the specified BigInt value + * @param scale the scale + * @return the constructed BigDecimal + */ + def apply(unscaledVal: BigInt, scale: Int): BigDecimal = apply(unscaledVal, scale, defaultMathContext) + def apply(unscaledVal: BigInt, scale: Int, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(unscaledVal.bigInteger, scale, mc), mc) + + def apply(bd: BigDec): BigDecimal = apply(bd, defaultMathContext) + def apply(bd: BigDec, mc: MathContext): BigDecimal = new BigDecimal(bd, mc) /** Implicit conversion from Int to BigDecimal. */ implicit def int2bigDecimal(i: Int): BigDecimal = apply(i) @@ -115,6 +145,11 @@ object BigDecimal * @since 2.8 */ implicit def bigInt2bigDecimal(x: BigInt): BigDecimal = apply(x) + + // Anyone can subclass Number, so we can't just assume .longValue is an unrounded + // representation (as it cannot be for anything larger than Long.) So we also confirm + // that at least x thinks it's equal to x.longValue. + private[scala] def equalsOwnLongValue(that: Number): Boolean = that == that.longValue } /** @@ -129,6 +164,7 @@ extends java.lang.Number { def this(bigDecimal: BigDec) = this(bigDecimal, BigDecimal.defaultMathContext) import BigDecimal.RoundingMode._ + import BigDecimal.equalsOwnLongValue /** Cuts way down on the wrapper noise. */ private implicit def bigdec2BigDecimal(x: BigDec): BigDecimal = new BigDecimal(x, mc) @@ -138,13 +174,16 @@ extends java.lang.Number /** Compares this BigDecimal with the specified value for equality. */ - override def equals(that: Any): Boolean = that match { - case that: BigDecimal => this equals that - case that: java.lang.Double => this.bigDecimal.doubleValue == that.doubleValue - case that: java.lang.Float => this.bigDecimal.floatValue == that.floatValue - case that: java.lang.Number => this equals BigDecimal(that.longValue) - case that: java.lang.Character => this equals BigDecimal(that.charValue.asInstanceOf[Int]) - case _ => false + override def equals (that: Any): Boolean = that match { + case that: BigDecimal => this equals that + case that: BigDec => this equals BigDecimal(that) + case that: BigInt => this equals BigDecimal(that) + case that: java.math.BigInteger => this equals BigDecimal(new BigInt(that), mc) + case that: java.lang.Double => this equals BigDecimal(that.doubleValue) + case that: java.lang.Float => this equals BigDecimal(that.floatValue) + case that: java.lang.Number => equalsOwnLongValue(that) && (this equals BigDecimal(that.longValue)) + case that: java.lang.Character => this equals BigDecimal(that.charValue.asInstanceOf[Int]) + case _ => false } /** Compares this BigDecimal with the specified BigDecimal for equality. @@ -327,6 +366,13 @@ extends java.lang.Number */ def toBigInt(): BigInt = new BigInt(this.bigDecimal.toBigInteger()) + /** Converts this BigDecimal to a scala.BigInt if it + * can be done losslessly, returning Some(BigInt) or None. + */ + def toBigIntExact(): Option[BigInt] = + try Some(new BigInt(this.bigDecimal.toBigIntegerExact())) + catch { case _: ArithmeticException => None } + /** Returns the decimal String representation of this BigDecimal. */ override def toString(): String = this.bigDecimal.toString() diff --git a/src/library/scala/BigInt.scala b/src/library/scala/BigInt.scala index ac8b4c40a7..abed8c748d 100644 --- a/src/library/scala/BigInt.scala +++ b/src/library/scala/BigInt.scala @@ -108,7 +108,9 @@ object BigInt { * @version 1.0, 15/07/2003 */ @serializable -class BigInt(val bigInteger: BigInteger) extends java.lang.Number { +class BigInt(val bigInteger: BigInteger) extends java.lang.Number +{ + import BigDecimal.equalsOwnLongValue /** Returns the hash code for this BigInt. */ override def hashCode(): Int = this.bigInteger.hashCode() @@ -116,12 +118,15 @@ class BigInt(val bigInteger: BigInteger) extends java.lang.Number { /** 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: java.lang.Double => this.bigInteger.doubleValue == that.doubleValue - case that: java.lang.Float => this.bigInteger.floatValue == that.floatValue - case that: java.lang.Number => this equals BigInt(that.longValue) - case that: java.lang.Character => this equals BigInt(that.charValue.asInstanceOf[Int]) - case _ => false + case that: BigInt => this equals that + case that: BigInteger => this equals new BigInt(that) + case that: BigDecimal => this equals that + case that: java.math.BigDecimal => this equals BigDecimal(that) + case that: java.lang.Double => this equals BigDecimal(that.doubleValue) + case that: java.lang.Float => this equals BigDecimal(that.floatValue) + case that: java.lang.Number => equalsOwnLongValue(that) && (this equals BigInt(that.longValue)) + case that: java.lang.Character => this equals BigInt(that.charValue.asInstanceOf[Int]) + case _ => false } /** Compares this BigInt with the specified BigInt for equality. @@ -129,6 +134,13 @@ class BigInt(val bigInteger: BigInteger) extends java.lang.Number { def equals (that: BigInt): Boolean = this.bigInteger.compareTo(that.bigInteger) == 0 + /** Compares this BigInt with the specified BigDecimal for equality. + */ + def equals(that: BigDecimal): Boolean = that.toBigIntExact match { + case None => false + case Some(x) => this equals x + } + /** Compares this BigInt with the specified BigInt */ def compare (that: BigInt): Int = this.bigInteger.compareTo(that.bigInteger) -- cgit v1.2.3