Ошибка в расчете BigDecimal - PullRequest
0 голосов
/ 23 ноября 2011

Рассмотрим следующую спецификацию:

require 'bigdecimal'

def total_percent(amounts)
  percent_changes = amounts.each_cons(2).map { |a|
    (a[1] - a[0]) / a[0] * BigDecimal.new('100.0')
  }
  (percent_changes.map { |pc| BigDecimal.new('1') + pc / BigDecimal.new('100') }.inject(BigDecimal.new('1'), :*) - BigDecimal.new('1')) * BigDecimal.new('100')
end

describe 'total_percent' do

  specify {
    values = [10000.0, 10100.0, 10200.0, 10000.0].map { |v|
      BigDecimal.new(v.to_s)
    }
    total_percent(values).class.should == BigDecimal
    total_percent(values).should == BigDecimal.new('0.0')
  }

end

Метод total_percent вычисляет общую разницу списка значений в процентах.Пожалуйста, не обращайте внимания на сам алгоритм (тот же результат может быть достигнут путем просмотра только первого и последнего значений).

Спецификация не выполняется, поскольку результат вычисления не равен 0.0.Вопрос в том, где теряется точность.

Редактировать: Использование JRuby 1.6.5 на OS X 10.7.2.

Ответы [ 2 ]

0 голосов
/ 24 ноября 2011

Это проблема с арифметикой с плавающей запятой.JRuby упаковывает класс Java BigDecimal в класс Ruby BigDecimal.Таким образом, значения BigDecimal содержат небольшие различия между текстовым представлением и фактическим значением.Вам не следует использовать == для их сравнения.

Обратите внимание, что эти спецификации не работают аналогично для МРТ.

0 голосов
/ 23 ноября 2011

Дело не в том, что он теряет точность, а в том, что некоторые из делений не могут быть представлены BigDecimal.

Вопрос в том, почему JRuby проглатывает / определяет исключение, которое следует выдавать, когдавы 100.0 / 10200.0 и т. д. JRuby может определить режим округления, или его операторы могут заключить себя в ловушку ArithmeticException, сгенерированного тем же вычислением (без режима округления) в Java (добавлено ниже).

Попробуйте установить собственный режим округления или сравните приемлемое значение дельта (я забыл термин).

Исключение

java.lang.ArithmeticException: Non-terminating decimal expansion;
    no exact representable decimal result.
...