2

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

Я передаю два значения, 0.205 и 0.205 (взятых из других чисел и sprintf'd) в модуль статистики и попрошу рассчитать распределение частоты, но он застревает в бесконечном цикле.

Пошаговое с помощью отладчика я могу видеть, что он делает:

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 (Диапазон макс-мин) возвращается 2.77555756156289e-17, а не 0, как я ожидал , Это означает, что цикл ((min + = range) < max)) вводит бесконечный цикл (для всех целей и задач).

DB < 8> print $ self -> {max};
0,205
DB < 9> print $ self -> {min};
0,205
DB < 10> print $ self -> {max} - $ self -> {min};
2.77555756156289e-17

Так что это похоже на проблему округления. Я не могу думать, как это исправить на моей стороне, но я не уверен, что редактирование библиотеки - хорошая идея. Я ищу предложения об обходном пути или альтернативе.

Приветствия, Нил

ответ

5

Я - статистика :: Описательный сопровождающий. Из-за его числового характера было сообщено много проблем округления. Я считаю, что этот конкретный был исправлен в более поздней версии той, которую вы использовали, которую я выпустил недавно, используя умножение для делений вместо + =.

Пожалуйста, используйте the most up-to-date version от CPAN, и это должно быть лучше.

+0

Hi, Shlomi! Рад, что вы заметили этот вопрос; вы избавили меня от необходимости отправлять вам по электронной почте ссылку. Я вижу, что новая версия все еще использует числа как хеш-ключи, такие как $ bins {$ self-> max()} = 0; чтобы избежать округления значений, вы можете использовать пакет «F» (требуется 5.8.0+) и распаковывать всякий раз, когда вы используете ключ. – ysth

+0

Отлично, спасибо! Я должен был проверить новую версию, по моей вине. Очень впечатлен этим ответом на мой первый вопрос о переполнении стека. Еще раз спасибо всем, кто ответил. – NeilInglis

3

Не совсем округления проблемы; Вы можете увидеть более точные значения с чем-то вроде

printf("%.18g %.18g", $self->{max}, $self->{min}); 

кажется мне, что есть ошибка в модуле, где она принимает диапазон выборки можно разделить на $ Перегородки штук; потому что плавающая точка не имеет бесконечной точности, это не всегда возможно. В вашем случае значения min и max являются точно смежными представляемыми значениями, поэтому не может быть более одного раздела. Я не знаю, для чего именно модуль использует разделы, поэтому я не уверен, каким может быть это воздействие. Еще одна проблема в модуле заключается в том, что он использует числа как хеш-ключи, которые неявно строит их, что немного округляет значение.

Вы можете иметь некоторый успех в отмывании данных через stringization перед подачей его к модулю:

$data = 0+"$data"; 

Это, по крайней мере убедиться, что два числа, что (с точностью печати по умолчанию) появляются равно фактически равны.

+0

Yup, спасибо. Макс фактически равен 0.20500000000000002, а минус 0.20499999999999999, поэтому это объясняет, почему это происходит неправильно. Я попробую некоторые обходные пути. – NeilInglis

-1

Это не должно вызывать бесконечный цикл. То, что вызовет бесконечный цикл, будет $self->{sample_range}/$partitions равно 0.

+0

Да, я так не думал DB <12> p $ iter; 0,205 DB <13> p $ interval; 3.46944695195361e-18 БД <14> р $ ITER + = $ Интервал 0,205 БД <15> р $ само -> {макс} 0,205 DB <16> р ($ ITER + = $ интервал) < $self-> {макс} так ((0.205 + 3.46944695195361e-18) <0.205) имеет значение true. Конечно, это был долгий день, поэтому я мог бы быть без мяча ... – NeilInglis

+0

Ошибка форматирования Hrm. Сожалею. – NeilInglis

+0

Nope; например, числа 1 и 1 + 2 ** - 52. Они различаются на 2 ** - 52. Предполагая, что вы хотите 4 раздела, что дает интервал 2 ** - 54 (что явно не равно нулю), но если вы попытаетесь добавить это значение в 1, вы оставите 1 неизменным (на большинстве платформ), так как ближайший представимый значение 1 + 2 ** - 54 равно 1. Цикл предполагает, что если вы увеличиваете число на ненулевое значение, это увеличит число, и в этом случае это неверно, что приведет к бесконечному циклу. – ysth