Scala 迭代和范围中的精度错误
Precision errors in Scala iterate and range
我正在尝试构建一个具有范围和步骤的双打集合。当我使用 Array.iterate
方法时,出现奇怪的浮点错误,例如:
scala> Array.iterate[Double](0.0, 10)(0.1+)
res0: Array[Double] = Array(0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999)
小步长的小范围会导致如此不精确,这似乎很奇怪。我知道还有其他方法可以做到这一点(例如 Array.iterate[Int](0, 10)(1+).map(i => i.toDouble / 10.0)
),但令我感到困惑的是内置的收集方法会执行得如此糟糕。这是有原因的还是我是个傻瓜,做错了?
这是浮点运算基本限制的症状,与集合无关:
scala> 0.2 + 0.1
res0: Double = 0.30000000000000004
有很多帖子解释了为什么会这样:
- What Every Computer Scientist Should Know About Floating-Point Arithmetic
- Why are floating point calculations so inaccurate?
- Why Are Floating Point Numbers Inaccurate?
尝试使用 BigDecimal:
scala> val r = BigDecimal(0) to BigDecimal(1) by BigDecimal(0.1)
scala> println(r)
NumericRange(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0)
我正在尝试构建一个具有范围和步骤的双打集合。当我使用 Array.iterate
方法时,出现奇怪的浮点错误,例如:
scala> Array.iterate[Double](0.0, 10)(0.1+)
res0: Array[Double] = Array(0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999)
小步长的小范围会导致如此不精确,这似乎很奇怪。我知道还有其他方法可以做到这一点(例如 Array.iterate[Int](0, 10)(1+).map(i => i.toDouble / 10.0)
),但令我感到困惑的是内置的收集方法会执行得如此糟糕。这是有原因的还是我是个傻瓜,做错了?
这是浮点运算基本限制的症状,与集合无关:
scala> 0.2 + 0.1
res0: Double = 0.30000000000000004
有很多帖子解释了为什么会这样:
- What Every Computer Scientist Should Know About Floating-Point Arithmetic
- Why are floating point calculations so inaccurate?
- Why Are Floating Point Numbers Inaccurate?
尝试使用 BigDecimal:
scala> val r = BigDecimal(0) to BigDecimal(1) by BigDecimal(0.1)
scala> println(r)
NumericRange(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0)