2017-01-26 11 views
-2

Я пытаюсь построить коллекцию парных разрядов, которая имеет диапазон и шаг. Когда я использую метод Array.iterate я получаю странные ошибки с плавающей запятой, как, например:Ошибки точности в Scala итерации и диапазон

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)), но я сбиваю с толку, что встроенный метод сбора будет работать так плохо. Есть ли причина для этого или я являюсь толстяком и делаю это неправильно?

+0

как это не ожидаемое поведение? Попробуйте запустить «0.1 + 0.1 + 0.1» в REPL, вы получите '0.30000000000000004' – Tim

ответ

1

Попробуйте использовать 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) 
+0

. В то время как вы можете использовать' BigDecimal' в таких диапазонах, как это, на самом деле это просто подметает основную проблему под ковриком (те 'BigDecimal 'экземпляры просто печатаются иначе, чем обычные десятичные числа, ошибка все еще существует). –

+0

Интересно, спасибо @ evan058. Из любопытства есть способ фактически бороться с ошибкой, а не «подметать ее под ковром»? –

+0

Используйте класс чисел «Rational», если это возможно, в противном случае выполняется пороговое значение, например 'BigDecimal'. Я очень рекомендую вам проверить хотя бы одну из статей, которые я опубликовал в своем ответе, понимание ограничений чисел с плавающей запятой абсолютно необходимо. –

3

Это симптом фундаментального ограничения арифметики с плавающей точкой и не имеет ничего общего с коллекциями:

scala> 0.2 + 0.1 
res0: Double = 0.30000000000000004 

Есть много сообщений, объясняющих, почему это происходит: