2

J'utilise la bibliothèque Statistics::Descriptive en Perl pour calculer les distributions de fréquence et faire face à un problème d'erreur d'arrondi en virgule flottante. Je passe deux valeurs, 0,205 et 0,205, (tirées d'autres nombres et sprintées vers celles-ci) au module stats et je lui demande de calculer la distribution de fréquence mais il se bloque dans une boucle infinie.Comment puis-je contourner une erreur d'arrondi qui provoque une boucle infinie dans Perl's Statistics :: Descriptive?

Parcourant avec un débogueur je peux voir que ça fait:

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 (La plage est max-min) est de retour 2.77555756156289e-17 plutôt que 0 comme j'attends . Cela signifie que la boucle ((min + = plage) < max)) entre dans une boucle infinie (à toutes fins utiles).

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

Cela ressemble à un problème d'arrondi. Je ne sais pas comment résoudre ça de mon côté, et je ne suis pas sûr que l'édition de la bibliothèque soit une bonne idée. Je cherche des suggestions d'une solution de contournement ou d'une alternative.

Cheers, Neil

Répondre

5

Je suis le Statistics :: Descriptive maintainer. En raison de sa nature numérique, de nombreux problèmes d'arrondis ont été signalés. Je crois que celui-ci a été corrigé dans une version ultérieure à celle que vous utilisiez que j'ai publiée récemment, en utilisant la multiplication pour les divisions au lieu de + =.

Veuillez utiliser the most up-to-date version du CPAN, et cela devrait être mieux.

+0

Salut, Shlomi! Content que tu aies remarqué cette question; vous m'avez sauvé de devoir vous envoyer un lien par courriel. Je vois que la nouvelle version utilise encore des nombres comme des clés de hachage comme $ bins {$ self-> max()} = 0; pour éviter d'avoir ce tour les valeurs, vous pouvez utiliser le pack "F" (nécessite 5.8.0+) et déballer chaque fois que vous utilisez la clé. – ysth

+0

Excellent, merci! J'aurais dû vérifier une nouvelle version, ma faute. Très impressionné par cette réponse à ma première question Stack Overflow. Merci encore à tous ceux qui ont répondu. – NeilInglis

3

Pas exactement un problème d'arrondi; vous pouvez voir les valeurs plus précises avec quelque chose comme

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

Me semble qu'il ya une faille dans le module où il assume la plage d'échantillon peut être divisé en partitions $ morceaux; parce que le point flottant n'a pas une précision infinie, ce n'est pas toujours possible. Dans votre cas, les valeurs min et max sont exactement des valeurs représentables adjacentes, il ne peut donc pas y avoir plus d'une partition. Je ne sais pas exactement ce que le module utilise pour les partitions, donc je ne suis pas sûr de l'impact que cela pourrait avoir. Un autre problème possible dans le module est qu'il utilise des nombres comme des clés de hachage, les stringifie implicitement ce qui arrondit légèrement la valeur.

Vous avez sans doute un certain succès dans le blanchiment de vos données par stringization avant de le nourrir au module:

$data = 0+"$data"; 

Cela permettra au moins faire en sorte que deux numéros (avec la précision d'impression par défaut) apparaissent égale sont en fait égal.

+0

Ouais, merci. Max est en fait 0,20500000000000002 et min est 0,20499999999999999, ce qui explique pourquoi cela ne va pas. Je vais essayer quelques solutions de contournement. – NeilInglis

-1

Cela ne devrait pas provoquer une boucle infinie. Ce qui ferait que cette boucle soit infinie serait $self->{sample_range}/$partitions est 0.

+0

Oui, je ne le pensais pas non plus mais DB <12> p $ iter; 0.205 DB <13> p $ intervalle; 3.46944695195361e-18 DB <14> p $ iter + = intervalle $ 0,205 DB <15> p $ self -> {max} 0,205 DB <16> p (iter $ + = intervalle de $) < $self-> {max} si ((0.205 + 3.46944695195361e-18) <0.205) évalue à vrai. Bien sûr, ça a été une longue journée pour que je puisse être hors de la balle ... – NeilInglis

+0

Le formatage Hrm échoue. Pardon. – NeilInglis

+0

Non; Prenez par exemple les chiffres 1 et 1 + 2 ** - 52. Ils diffèrent de 2 ** - 52. En supposant que vous voulez 4 partitions, cela donne un intervalle de 2 ** - 54 (qui est clairement non nul), mais si vous essayez d'ajouter cela à 1, vous laissez le 1 inchangé (sur la plupart des plateformes), puisque le plus représentable valeur à 1 + 2 ** - 54 est 1. La boucle suppose que si vous incrémentez un nombre par une valeur non nulle, cela augmentera le nombre, ce qui n'est pas vrai dans ce cas, conduisant à une boucle sans fin. – ysth

Questions connexes