summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-10-19 22:17:19 +0000
committerPaul Phillips <paulp@improving.org>2009-10-19 22:17:19 +0000
commit1e452efbc159bcd553871a44cb697413cc6fc1bd (patch)
treebc06eaf8c6f8b3f3bdd38552943a479faeaff692 /src
parent5eeb2a3b439419e1f6b17925c439346a3cab017d (diff)
downloadscala-1e452efbc159bcd553871a44cb697413cc6fc1bd.tar.gz
scala-1e452efbc159bcd553871a44cb697413cc6fc1bd.tar.bz2
scala-1e452efbc159bcd553871a44cb697413cc6fc1bd.zip
Another bunch of work on Numeric, Range, Generi...
Another bunch of work on Numeric, Range, GenericRange, BigDecimal, and a pile of test cases for various aspects of GenericRange.
Diffstat (limited to 'src')
-rw-r--r--src/library/scala/BigDecimal.scala17
-rw-r--r--src/library/scala/Numeric.scala21
-rw-r--r--src/library/scala/collection/immutable/GenericRange.scala67
-rw-r--r--src/library/scala/collection/immutable/Range.scala18
-rw-r--r--src/library/scala/runtime/RichDouble.scala20
5 files changed, 103 insertions, 40 deletions
diff --git a/src/library/scala/BigDecimal.scala b/src/library/scala/BigDecimal.scala
index 39b3f4b571..fc874db1f2 100644
--- a/src/library/scala/BigDecimal.scala
+++ b/src/library/scala/BigDecimal.scala
@@ -46,6 +46,15 @@ object BigDecimal
val defaultMathContext = MathContext.UNLIMITED
+ /** Constructs a <code>BigDecimal</code> using the java BigDecimal static
+ * valueOf constructor.
+ *
+ * @param d the specified double value
+ * @return the constructed <code>BigDecimal</code>
+ */
+ def valueOf(d: Double): BigDecimal = apply(BigDec valueOf d)
+ def valueOf(d: Double, mc: MathContext): BigDecimal = apply(BigDec valueOf d, mc)
+
/** Constructs a <code>BigDecimal</code> whose value is equal to that of the
* specified <code>Integer</code> value.
*
@@ -144,7 +153,7 @@ object BigDecimal
implicit def long2bigDecimal(l: Long): BigDecimal = apply(l)
/** Implicit conversion from <code>Double</code> to <code>BigDecimal</code>. */
- implicit def double2bigDecimal(d: Double): BigDecimal = apply(d)
+ implicit def double2bigDecimal(d: Double): BigDecimal = valueOf(d, defaultMathContext)
}
/**
@@ -337,9 +346,9 @@ extends jl.Number with ScalaNumericConversions
def floatValue = this.bigDecimal.floatValue
/** Converts this BigDecimal to a <tt>Double</tt>.
- * if this BigDecimal has too great a magnitude to represent as a float,
- * it will be converted to <code>Float.NEGATIVE_INFINITY</code> or
- * <code>Float.POSITIVE_INFINITY</code> as appropriate.
+ * if this BigDecimal has too great a magnitude to represent as a double,
+ * it will be converted to <code>Double.NEGATIVE_INFINITY</code> or
+ * <code>Double.POSITIVE_INFINITY</code> as appropriate.
*/
def doubleValue = this.bigDecimal.doubleValue
diff --git a/src/library/scala/Numeric.scala b/src/library/scala/Numeric.scala
index ef3d771d6e..3048f9287d 100644
--- a/src/library/scala/Numeric.scala
+++ b/src/library/scala/Numeric.scala
@@ -104,11 +104,10 @@ object Numeric {
}
implicit object FloatIsFractional extends FloatIsFractional with Ordering.FloatOrdering
- trait DoubleIsFractional extends Fractional[Double] {
+ trait DoubleIsConflicted extends Numeric[Double] {
def plus(x: Double, y: Double): Double = x + y
def minus(x: Double, y: Double): Double = x - y
def times(x: Double, y: Double): Double = x * y
- def div(x: Double, y: Double): Double = x / y
def negate(x: Double): Double = -x
def fromInt(x: Int): Double = x
def toInt(x: Double): Int = x.toInt
@@ -116,7 +115,13 @@ object Numeric {
def toFloat(x: Double): Float = x.toFloat
def toDouble(x: Double): Double = x
}
- implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.DoubleOrdering
+ trait DoubleIsFractional extends DoubleIsConflicted with Fractional[Double] {
+ def div(x: Double, y: Double): Double = x / y
+ }
+ trait DoubleAsIfIntegral extends DoubleIsConflicted with Integral[Double] {
+ def quot(x: Double, y: Double): Double = (BigDecimal(x) / BigDecimal(y)).doubleValue
+ def rem(x: Double, y: Double): Double = (BigDecimal(x) remainder BigDecimal(y)).doubleValue
+ }
trait BigDecimalIsConflicted extends Numeric[BigDecimal] {
def plus(x: BigDecimal, y: BigDecimal): BigDecimal = x + y
@@ -135,14 +140,16 @@ object Numeric {
}
trait BigDecimalAsIfIntegral extends BigDecimalIsConflicted with Integral[BigDecimal] {
def quot(x: BigDecimal, y: BigDecimal): BigDecimal = x / y
- // scala.BigDecimal doesn't give access to remainder, grr
- def rem(x: BigDecimal, y: BigDecimal): BigDecimal =
- new BigDecimal(x.bigDecimal remainder y.bigDecimal)
+ def rem(x: BigDecimal, y: BigDecimal): BigDecimal = x remainder y
}
- // The Fractional one is the implicit, but Integral is useful for GenericRange.
+ // For Double and BigDecimal we offer implicit Fractional objects, but also one
+ // which acts like an Integral type, which is useful in GenericRange.
implicit object BigDecimalIsFractional extends BigDecimalIsFractional with Ordering.BigDecimalOrdering
object BigDecimalAsIfIntegral extends BigDecimalAsIfIntegral with Ordering.BigDecimalOrdering
+
+ implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.DoubleOrdering
+ object DoubleAsIfIntegral extends DoubleAsIfIntegral with Ordering.DoubleOrdering
}
trait Numeric[T] extends Ordering[T] {
diff --git a/src/library/scala/collection/immutable/GenericRange.scala b/src/library/scala/collection/immutable/GenericRange.scala
index 90779912c0..1fd977d7e8 100644
--- a/src/library/scala/collection/immutable/GenericRange.scala
+++ b/src/library/scala/collection/immutable/GenericRange.scala
@@ -8,13 +8,12 @@
// $Id: GenericRange.scala 18987 2009-10-08 18:31:44Z odersky $
-package scala.collection.immutable
+package scala.collection
+package immutable
import annotation.experimental
-
-import collection.VectorView
-import util.control.Exception.catching
-import util.Hashable
+import mutable.{ Builder, ListBuffer }
+import generic._
/** <p>
* <code>GenericRange</code> is a generified version of the
@@ -39,15 +38,21 @@ import util.Hashable
abstract class GenericRange[+T]
(val start: T, val end: T, val step: T, val isInclusive: Boolean)
(implicit num: Integral[T])
-extends VectorView[T, collection.immutable.Vector[T]]
+extends Vector[T]
{
import num._
+ private def fail(msg: String) = throw new UnsupportedOperationException(msg)
+
+ if (step equiv zero)
+ fail("GenericRange step cannot be zero.")
+
// todo? - we could lift the length restriction by implementing a range as a sequence of
// subranges and limiting the subranges to MAX_INT. There's no other way around it because
// the generics we inherit assume integer-based indexing (as well they should.)
- require(!(step equiv zero))
- require(genericLength <= fromInt(Math.MAX_INT), "Implementation restricts ranges to Math.MAX_INT elements.")
+ // The second condition is making sure type T can meaningfully be compared to Math.MAX_INT.
+ if (genericLength > fromInt(Math.MAX_INT) && (Math.MAX_INT == toInt(fromInt(Math.MAX_INT))))
+ fail("Implementation restricts ranges to Math.MAX_INT elements.")
// inclusive/exclusiveness captured this way because we do not have any
// concept of a "unit", we can't just add an epsilon to an exclusive
@@ -97,7 +102,7 @@ extends VectorView[T, collection.immutable.Vector[T]]
}
def length: Int = toInt(genericLength)
- final override def isEmpty =
+ override def isEmpty =
if (step > zero)
if (isInclusive) end < start
else end <= start
@@ -123,6 +128,46 @@ extends VectorView[T, collection.immutable.Vector[T]]
)
}
+ // Motivated by the desire for Double ranges with BigDecimal precision,
+ // we need some way to map a Range and get another Range. This can't be
+ // done in any fully general way because Ranges are not arbitrary
+ // sequences but step-valued, so we have a custom method only we can call
+ // which we promise to use responsibly.
+ //
+ // The point of it all is that
+ //
+ // 0.0 to 1.0 by 0.1
+ //
+ // should result in
+ //
+ // GenericRange[Double](0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0)
+ //
+ // and not
+ //
+ // GenericRange[Double](0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9)
+ //
+ // or perhaps more importantly,
+ //
+ // (0.1 to 0.3 by 0.1 contains 0.3) == true
+ //
+ private[immutable] def mapRange[A](fm: T => A)(implicit unum: Integral[A]): GenericRange[A] = {
+ val self = this
+
+ // XXX This may be incomplete.
+ new GenericRange[A](fm(start), fm(end), fm(step), isInclusive) {
+ def copy[A1 >: A](start: A1, end: A1, step: A1)(implicit unum: Integral[A1]): GenericRange[A1] =
+ if (isInclusive) GenericRange.inclusive(start, end, step)
+ else GenericRange(start, end, step)
+
+ private val underlyingRange: GenericRange[T] = self
+ override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) }
+ override def isEmpty = underlyingRange.isEmpty
+ override def apply(idx: Int): A = fm(underlyingRange(idx))
+ override def containsTyped[A1 >: A](el: A1)(implicit unum: Integral[A1]) =
+ underlyingRange exists (x => fm(x) == el)
+ }
+ }
+
// The contains situation makes for some interesting code.
// I am not aware of any way to avoid a cast somewhere, because
// contains must take an Any.
@@ -153,9 +198,7 @@ extends VectorView[T, collection.immutable.Vector[T]]
}
}
-
-object GenericRange
-{
+object GenericRange {
class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T])
extends GenericRange(start, end, step, true) {
def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): Inclusive[U] =
diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala
index 56564a28aa..2b0b7200de 100644
--- a/src/library/scala/collection/immutable/Range.scala
+++ b/src/library/scala/collection/immutable/Range.scala
@@ -177,10 +177,22 @@ object Range {
GenericRange.inclusive(start, end, step)
}
- // Double re-uses BigDecimal's range.
+ // Double works by using a BigDecimal under the hood for precise
+ // stepping, but mapping the sequence values back to doubles with
+ // .doubleValue. This constructs the BigDecimals by way of the
+ // String constructor (valueOf) instead of the Double one, which
+ // is necessary to keep 0.3d at 0.3 as opposed to
+ // 0.299999999999999988897769753748434595763683319091796875 or so.
object Double {
- def apply(start: Double, end: Double, step: Double) = scala.BigDecimal(start) until end by step
- def inclusive(start: Double, end: Double, step: Double) = scala.BigDecimal(start) to end by step
+ implicit val bigDecAsIntegral = scala.Numeric.BigDecimalAsIfIntegral
+ implicit val doubleAsIntegral = scala.Numeric.DoubleAsIfIntegral
+ def toBD(x: Double): BigDecimal = scala.BigDecimal valueOf x
+
+ def apply(start: Double, end: Double, step: Double) =
+ BigDecimal(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue)
+
+ def inclusive(start: Double, end: Double, step: Double) =
+ BigDecimal.inclusive(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue)
}
// As there is no appealing default step size for not-really-integral ranges,
diff --git a/src/library/scala/runtime/RichDouble.scala b/src/library/scala/runtime/RichDouble.scala
index 3d18aefce2..c29d7e0868 100644
--- a/src/library/scala/runtime/RichDouble.scala
+++ b/src/library/scala/runtime/RichDouble.scala
@@ -27,29 +27,21 @@ final class RichDouble(x: Double) extends Proxy with Ordered[Double] {
def ceil: Double = Math.ceil(x)
def floor: Double = Math.floor(x)
- /** !!! At the time I wrote these Double ranges, BigDecimal(0.17) == 0.17d. Since
- * this is no longer true, these ranges need to operate on Doubles, else
- * (0.0 to 1.0 by 0.1).contains(0.1) will be false under the anticipated 2.8
- * equality scheme. They only operate on BigDecimals because I wasn't 100%
- * sure about when operations on Doubles are guaranteed to be exact and using
- * BigDecimal seemed the conservative route.
- */
-
/** See <code>BigDecimal.until</code>. */
- def until(end: Double): Range.Partial[Double, GenericRange.Exclusive[BigDecimal]] =
+ def until(end: Double): Range.Partial[Double, GenericRange[Double]] =
new Range.Partial(until(end, _))
/** See <code>BigDecimal.until</code>. */
- def until(end: Double, step: Double): GenericRange.Exclusive[BigDecimal] =
- BigDecimal(x).until(end, step)
+ def until(end: Double, step: Double): GenericRange[Double] =
+ Range.Double(x, end, step)
/** See <code>BigDecimal.to</code>. */
- def to(end: Double): Range.Partial[Double, GenericRange.Inclusive[BigDecimal]] =
+ def to(end: Double): Range.Partial[Double, GenericRange[Double]] =
new Range.Partial(to(end, _))
/** See <code>BigDecimal.to</code>. */
- def to(end: Double, step: Double): GenericRange.Inclusive[BigDecimal] =
- BigDecimal(x).to(end, step)
+ def to(end: Double, step: Double): GenericRange[Double] =
+ Range.Double.inclusive(x, end, step)
/** Converts an angle measured in degrees to an approximately equivalent
* angle measured in radians.