scala - 使用RichDouble.to操作获取NumericRange

标签 scala

我对此ArithmeticException感到有些困惑。我已经在Scala 2.8.0.RC6和RC7上尝试过此方法。

scala> 7.12 to(8, 0.2)
res0: scala.collection.immutable.NumericRange[Double] = NumericRange(7.12, 7.32, 7.52, 7.72, 7.92)

scala> 7.12 to(8, 0.5)
res2: scala.collection.immutable.NumericRange[Double] = NumericRange(7.12, 7.62)

scala> 7.12 to(8, 0.3)
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
 at java.math.BigDecimal.divide(BigDecimal.java:1525)
 at java.math.BigDecimal.divide(BigDecimal.java:1558)
 at scala.math.BigDecimal.$div(BigDecimal.scala:228)
 at scala.math.Numeric$BigDecimalAsIfIntegral$class.quot(Numeric.scala:156)
 at scala.math.Numeric$BigDecimalAsIfIntegral$.quot(Numeric.scala:163)
 at scala.math.Numeric$BigDecimalAsIfIntegral$.quot(Numeric.scala:163)
 at scala.math.Integral$IntegralOps.$div$percent(Integral.scala:23)
 at scala.collection.immutable.NumericRange.genericLength(NumericRange.scala:104)
 at scala.collection.immutable.NumericRange.<init>(NumericRange.scala:63)
 at scala.collection.immutable.NumericRange$Inclusive.<init>(NumericRange.scala:209)
 at ...

最佳答案

这个BigDecimal的确切来源是什么?以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)
  }

并移至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)
    }
  }

如果toBD使用允许舍入的MathContext,天空会掉下来吗?两种弊端中哪一种较小?我将这个问题推迟到@extempore。

关于scala - 使用RichDouble.to操作获取NumericRange,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3246903/

相关文章:

用于类型转换的 Scala 多态性

Scala 3 宏 - 在运行时保留泛型

scala - 意外的 "recursive value needs type"编译错误(由本地隐式触发)

scala - 当存在同名本地成员时从外部范围访问值

scala - Play (Scala)在同一 Action 中处理不同的内容类型

session - 访问 Play 的正确方法! Scala 模板中的缓存?

Scala 要么带单位

class - Scala 自动 getter 和 setter 覆盖自定义 _=

scala - 在 Spark 中生成大量随机数据的有效方法

scala - 项目导入需要SBT 0.12.4+