en jouant avec un exemple de récursion de la queue, j'ai remarqué une faible divergence entre les résultats d'un appel récursif normal et un appel récursif de queue: juste hors de curiosité, peut-on expliquer à moi pourquoi ou où l'arrondi se passe. Je suppose que parce que le compilateur Scala traduit la version récursive de la queue à une boucle, le paramètre ACC code> est attribué à chaque itération de la boucle et que la petite erreur d'arrondi glisse là-bas. P> p>
3 Réponses :
Vos deux fonctions ne font pas les opérations dans le même ordre.
in c: p> impressions: p> (j'ai utilisé une plate-forme sur laquelle le point flottant en C fonctionne de manière prévisible) p> une version de votre fonction calcule Si je devais deviner, je m'attendrais à ce que 30. * 29 * ... code> et l'autre Calcules 2. * 3 * ... code>.
Il est normal que ces deux résultats soient légèrement différents: les opérations à virgule flottante ne sont pas associatives. Mais veuillez noter qu'il n'y a rien d'insondable sur les résultats. Une de vos fonctions calcule 30. * 29 * ... code> et l'autre calcule exactement fort> 2. * 3 * ... code>. Ils travaillent tous deux comme conçu. P> 2. * 3 * ... code> est plus précis (plus proche du résultat obtenu avec des nombres réels ), mais peu importe: les deux chiffres sont très proches et très proches du résultat réel. p> p>
+1. Chose étrange, mais je viens d'essayer la même chose en C #. Les deux implémentations renvoient exactement des résultats égaux.
Merci pour la grande comparaison avec C. J'ai suscité votre réponse, mais j'ai marqué le nouveau gars aussi correct puisqu'il a moins de représentant et est également correct. J'espère que ça va.
@JacobusR Great Idée. Pour la comparaison avec c, la cause réelle était que je n'ai pas eu de compilateur Scala à portée de main, mais si elle contribue à dissiper le mythe de l'imprévisibilité du point flottant, tant mieux :)
Le résultat est différent car les deux versions font les multiplications dans un ordre différent, conduisant ainsi à différents arrondis. P>
L'appel récursif normal conduit à une expression Essayez de réécrire une des versions afin qu'elle itère l'inverse de l'autre sens et que vous devriez théoriquement, obtenir la même réponse. P> n * ([n-1] * ([n-2] * (...))) code>, car vous calculez d'abord la valeur du fait (n-1) puis multipliez-le avec N, tandis que la queue récursive conduit à ((N * [n-1]) * [N-2]) * ... code> parce que vous avez d'abord multiplier par n puis itérer sur N-1. P>
La différence ne concerne pas le fait que Scala allume la récursion de la queue en boucle. Le résultat serait le même sans cette optimisation. La récursion n'agit également pas différemment en ce qui concerne les erreurs d'arrondi que les boucles. P>
La différence est l'ordre dans lequel les chiffres sont multipliés. Votre première solution recouvre jusqu'au bout jusqu'à 1 avant de commencer à multiplier les chiffres. Donc, il finira par calculer Bien sûr, nous dirions que ces deux sont identiques parce que la multiplication est associative, mais ce n'est pas vrai pour l'arithmétique à virgule flottante. Utilisation de points flottants Notez que vous verrez la même différence lors de l'utilisation d'une boucle pour la boucle qui compte de 1 à n versus un qui compte de N à 1 pour mettre en œuvre la factorielle. P> n * ((n-1) * (... * (2 * 1))) code>. La version récursive de la queue commence à se multiplier tout de suite, il finit donc à calculer n * (n-1) * ... * 2 * 1 code>. P>.
(x * y) * z code> peut très bien être différent de x * (y * z) code> car les erreurs d'arrondi se propagent différemment. Donc cela explique votre comportement. P>
Dans un langage de programmation approprié, comme Scala est, attribuant un
double code> résultat à une variable code> double code> n'introduit pas une erreur d'arrondi.