斯卡拉范围分裂错过了最后一个

时间:2016-06-14 07:31:54

标签: scala

我使用scala Range.by来分割一个范围以获得一个数组,但它错过了一些特殊存储桶数的最后一个,例如100.我很困惑,并演示如下:

object SplitDemo extends App {
  val min = 0.0
  val max = 7672.142857142857
  val bucketNum = 100

  def splitsBucket1(min: Double, max: Double, num: Int) = (min to max by ((max - min) / num)).toArray
  def splitsBucket2(min: Double, max: Double, num: Int): Array[Double] = {
    val rst = Array.fill[Double](num + 1)(0)
    rst(0) = min
    rst(num) = max

    val step = (max-min)/num
    for(i <- 1 until num) rst(i) = rst(i-1)+step

    rst
  }

  val split1 = splitsBucket1(min, max, bucketNum)
  println(s"Split1 size = ${split1.size}, %s".format(split1.takeRight(4).mkString(",")))

  val split2 = splitsBucket2(min, max, bucketNum)
  println(s"Split2 size = ${split2.size}, %s".format(split2.takeRight(4).mkString(",")))

}

输出正在跟随

  

Split1尺寸= 100,7365.257142857143,7441.978571428572,7518.700000000001,7595.421428571429
  拆分2尺寸= 101,7441.978571428588,7518.700000000017,7595.421428571446,7672.142857142857

当num = 100时,split1 错过了最后一个,但是split2没有(这是我的期望)。当num是其他num时,e.t。 130,split1和split2得到样本结果 是什么原因可以说明差异?

1 个答案:

答案 0 :(得分:2)

通常是floating point inaccuracy

看一下,max在分割并乘以它后如何以不同方式出现:

scala> 7672.142857142857 / 100 * 100
res1: Double = 7672.142857142858

此数字大于max,因此不适合该范围:

scala> max / bucketNum * bucketNum > max
res2: Boolean = true

它比在step中添加splitsBucket2 100次更正确:

scala> var result = 0.0
result: Double = 0.0

scala> for (_ <- 0 until 100) result += (max - min) / bucketNum

scala> result
res4: Double = 7672.142857142875

这比maxmax / bucketNum * bucketNum都要大。您可以通过明确指定splitBuckets2来在rst(num) = max中避免这种情况。

您可以尝试以下拆分实现:

def splitsBucket3(min: Double, max: Double, num: Int): Array[Double] = {
  val step = (max - min) / num
  Array.tabulate(num + 1)(min + step * _)
}

保证具有正确数量的元素,并且具有比splitsBucket2更少的数值精度问题。