42 votes

Haskell gammes et flotteurs

Pourquoi le comportement de la notation d'intervalle Haskell pour les flottants est-il différent de celui des entiers et des caractères?

 Prelude> [1, 3 .. 10] :: [Int]
[1,3,5,7,9] 
Prelude> [1, 3 .. 10] :: [Float]
[1.0,3.0,5.0,7.0,9.0,11.0]
Prelude> ['a', 'c' .. 'f']
"ace"
 

Je comprendrais si le dernier élément était proche de la limite supérieure, mais ce n’est évidemment pas une question d’arrondi.

37voto

hammar Points 89293

La syntaxe [e1, e2 .. e3] est vraiment sucre syntaxique pour enumFromThenTo e1 e2 e3, ce qui est une fonction dans l' Enum typeclass.

Le Haskell norme définit sa sémantique comme suit:

Pour les types Int et Integer, l'énumération des fonctions ont l' signification suivante:

  • La séquence de enumFrom e1 est la liste [e1,e1 + 1,e1 + 2,…].
  • La séquence de enumFromThen e1 e2 est la liste [e1,e1 + i,e1 + 2i,…], où l'incrément, i, est - e2 − e1. L'incrément peut être zéro ou négatif. Si l'incrément est de zéro, tous les éléments de la liste sont les de même.
  • La séquence de enumFromTo e1 e3 est la liste [e1,e1 + 1,e1 + 2,…e3]. La liste est vide si e1 > e3.
  • La séquence de enumFromThenTo e1 e2 e3 est la liste [e1,e1 + i,e1 + 2i,…e3], où l'incrément, i, est - e2 − e1. Si l'incrément est positive ou nulle, la liste se termine lorsque l'élément suivant serait plus de e3; la liste est vide si e1 > e3. Si l'incrément est négatif, la liste se termine lorsque l'élément suivant serait de moins de e3; la liste est vide si e1 < e3.

C'est à peu près ce que vous attendez, mais l' Float et Double instances sont définies différemment:

Pour Float et Double, la sémantique de l' enumFrom famille est donné par les règles d' Int ci-dessus, sauf que la liste se termine lorsque les éléments sont plus qu' e3 + i∕2 par incrément positif i, ou quand ils sont à moins de e3 + i∕2 pour le négatif, i.

Je ne suis pas vraiment sûr de ce que la justification de ce qui est, donc la seule réponse que je puisse vous donner est que c'est de cette façon parce qu'il est défini dans la norme.

Vous pouvez contourner ce problème en énumérant à l'aide de nombres entiers et de conversion d' Float par la suite.

Prelude> map fromIntegral [1, 3 .. 10] :: [Float]
[1.0,3.0,5.0,7.0,9.0]

13voto

leftaroundabout Points 23679

Ok, @Henning Makholm déjà dit dans son commentaire, mais il n'a pas d'expliquer pourquoi c'est vraiment une meilleure solution.

Première chose à dire: quand on traite avec virgule flottante, nous devons toujours être conscient de la possibilité d'erreurs d'arrondi. Lorsque nous écrivons, [0.0, 0.1 .. 1.0] nous devons être conscients que tous ces chiffres, sauf pour la première, qui ne sera pas sur les lieux exacts de dixièmes. Où nous avons besoin de ce genre de certitude, nous ne devons pas utiliser les flotteurs à tous.

Mais bien sûr, il existe de nombreuses applications pour lesquelles nous sommes le contenu avec raisonnable certainy, mais besoin de haut débit. C'est là que les flotteurs sont grands. Une application possible d'une telle liste pourrait être une simple trapèze intégration numérique:

trIntegrate f l r s = sum [ f x | x<-[l,(l+s)..r] ] * s - (f(l)+f(r))*s/2

nous allons tester ceci: trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1 => 25.797334337026466
par rapport à 25.9144 une erreur de moins de un pour cent. Elle n'est pas exacte, bien sûr, mais c'est inhérent à la méthode de l'intégration.

Supposons maintenant que le flotteur plages ont été définis afin de toujours mettre fin à lors du passage de la bordure droite. Ensuite, il serait possible (mais on ne peut pas en être certain!) que seulement 20 valeurs plutôt que de 21 sont calculés dans la somme, parce que la dernière valeur de x arrive à être 3.000000 quelque chose. On peut simuler cette

bad_trIntegrate f l r s = sum [ f x | x<-[l,(l+s)..(r-s)] ] * s - (f(l)+f(r))*s/2

puis, nous avons

bad_trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1

=> 21.27550564546988
urgh!

Cela n'a rien à voir avec de cacher les problèmes avec virgule flottante. C'est juste une méthode pour aider le programmeur à obtenir autour de ces problèmes plus facile. En fait, le résultat contre-intuitif de l' [1, 3 .. 10] :: Float aide à se souvenir de ces problèmes!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X