-2

I'm trying to build a collection of Doubles that has a range and a step. When I use the Array.iterate method I get strange floating point errors like such:

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)

It seems odd that a small range with a small step would cause such imprecision. I'm aware there are other ways I could do this (e.g. Array.iterate[Int](0, 10)(1+).map(i => i.toDouble / 10.0)) but I'm baffled that a built in collection method would perform so badly. Is there a reason for this or am I being a dolt and doing it the wrong way?

the-jackalope
  • 371
  • 1
  • 10
  • how is this not expected behavior? Try running `0.1 + 0.1 + 0.1` in REPL, you'll get `0.30000000000000004` – Tim Jan 26 '17 at 23:20

2 Answers2

4

This is a symptom of a fundamental limitation of floating point arithmetic and has nothing to do with collections:

scala> 0.2 + 0.1
res0: Double = 0.30000000000000004

There are many posts explaining why this happens:

Community
  • 1
  • 1
evan.oman
  • 5,922
  • 22
  • 43
2

Try using 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)
Tim
  • 3,675
  • 12
  • 25
  • While is neat that you can use `BigDecimal` in ranges like this, it is really just sweeping the underlying issue under the rug (those `BigDecimal` instances are just printing differently than the regular decimal, the error is still there). – evan.oman Jan 26 '17 at 23:32
  • Interesting, thanks @evan058 . Out of curiosity is there a way to actually combat the error rather than "sweeping it under the rug"? – the-jackalope Jan 26 '17 at 23:42
  • Use a `Rational` numbers class if possible, otherwise a threshold like `BigDecimal` is doing. I _highly_ recommend you check out at least one of the articles I posted in my answer, understanding the limitations of floating point numbers is absolutely essential – evan.oman Jan 26 '17 at 23:44
  • Note on `BigDecimal`: `scala> val a = BigDecimal(.1) + BigDecimal(.2) ==> a: scala.math.BigDecimal = 0.3` but then `scala> a.isExactDouble res14: Boolean = false`. See definition of [`isExactDouble`](http://www.scala-lang.org/api/2.12.x/scala/math/BigDecimal.html#isExactDouble:Boolean). – evan.oman Jan 26 '17 at 23:47
  • Great - thanks a lot! I'll check out your articles too – the-jackalope Jan 26 '17 at 23:50