Where does this BigDecimal come from? Start with Range.scala
// 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 { 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) }
And go to NumericRange.scala :
// 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 // // NumericRange[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 // // NumericRange[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]): NumericRange[A] = { val self = this // XXX This may be incomplete. new NumericRange[A](fm(start), fm(end), fm(step), isInclusive) { def copy(start: A, end: A, step: A): NumericRange[A] = if (isInclusive) NumericRange.inclusive(start, end, step) else NumericRange(start, end, step) private val underlyingRange: NumericRange[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(el: A) = underlyingRange exists (x => fm(x) == el) } }
Would the sky not fall if toBD used a MathContext that allowed rounding? What is the lesser of two evils? I will postpone this question at @extempore.