Как я могу обойти ошибку округления, которая вызывает бесконечный цикл в Perl Statistics::Descriptive?
-
09-09-2019 - |
Вопрос
Я использую Статистика::Описательная библиотека на Perl для вычисления частотных распределений и решения проблемы с ошибкой округления с плавающей запятой.
Я передаю два значения, 0.205 и 0.205, (взятые из других чисел и спринтированные с ними) в модуль статистики и прошу его вычислить распределение частот, но он застревает в бесконечном цикле.
Проходя через отладчик, я вижу, что он делает:
my $interval = $self->{sample_range}/$partitions;
my $iter = $self->{min};
while (($iter += $interval) < $self->{max}) {
$bins{$iter} = 0;
push @k, $iter; ##Keep the "keys" unstringified
}
$self->sample_range (диапазон max-min) возвращает 2.77555756156289e-17, а не 0, как я ожидал.Это означает, что цикл ((min+=диапазон) < max)) входит в (по сути) бесконечный цикл.
ДБ<8> вывести $self->{max};
0.205
ДБ<9> вывести $self->{min};
0.205
ДБ<10> вывести $self->{max}-$self->{min};
2.77555756156289е-17
Так что это похоже на проблему округления.Однако я не могу придумать, как исправить это со своей стороны, и я не уверен, что редактирование библиотеки - хорошая идея.Я ищу предложения по обходному пути или альтернативе.
Приветствия, Нил
Решение
Я сопровождающий Statistics::Descriptive.Из-за его числовой природы сообщалось о многих проблемах с округлением.Я полагаю, что этот конкретный вариант был исправлен в более поздней версии по сравнению с той, которую вы использовали и которую я недавно выпустил, используя умножение для делений вместо +=.
Пожалуйста, используйте самая актуальная версия из CPAN, и это должно быть лучше.
Другие советы
Не совсем проблема округления;вы можете увидеть более точные значения с помощью чего-то вроде
printf("%.18g %.18g", $self->{max}, $self->{min});
Мне кажется, что в модуле есть недостаток, из-за которого предполагается, что диапазон выборки может быть разделен на части $ partitions;поскольку плавающая точка не обладает бесконечной точностью, это не всегда возможно.В вашем случае минимальные и максимальные значения являются точно смежными представимыми значениями, поэтому не может быть более одного раздела.Я не знаю, для чего именно модуль использует разделы, поэтому я не уверен, как это может повлиять.Другая возможная проблема в модуле заключается в том, что он использует числа в качестве хэш-ключей, что неявно преобразует их в строку, что слегка округляет значение.
Вы можете иметь некоторый успех в борьбе с отмыванием ваши данные через stringization перед подачей модуль:
$data = 0+"$data";
Это, по крайней мере, гарантирует, что два числа, которые (с точностью печати по умолчанию) кажутся равными, на самом деле равны.
Это не должно приводить к бесконечному циклу.Что привело бы к тому, что этот цикл был бы бесконечным, было бы, если $self->{sample_range}/$partitions
равно 0.