0

L'exemple suivant met en évidence un écueil en ce qui concerne l'utilisation des nombres à virgule flottante:Planification d'événements lorsque le temps est une valeur en virgule flottante

available_again = 0 
for i in range(0,15): 
    time = 0.1*i 

    if time < available_again: 
     print("failed to schedule at " + str(time)) 

    available_again = time + 0.1 

Ce code produit les éléments suivants:

failed to schedule at 1.3 

Je n'étais Je ne m'attends pas à cette erreur, mais je comprends pourquoi cela se produit. Quelles options ai-je pour résoudre ce problème?

Une solution dans mon code serait:

available_again = 0.1*(i+1) 

Je me demande si cela est la bonne voie. Mon application particulière implique la planification d'événements où l'heure à laquelle les événements se produisent est dictée par des fonctions mathématiques complexes, par exemple: sinc (2 * pi * f * t). La durée des événements sera telle que les événements peuvent se chevaucher, auquel cas je devrai les envoyer sur des canaux séparés.

+0

Cela peut paraître évident, mais je pense que la seule façon fiable d'y parvenir est d'utiliser des entiers (et de définir l'unité en millisecondes, en microsecondes ou autre). Peut-être pouvez-vous redimensionner vos horodatages complexes et les utiliser comme des entiers (comme 'int (1000 * sin (2 * pi * f * t)')? – jdehesa

Répondre

1

Une solution dans mon code serait:

available_again = 0,1 * (i + 1)

Ce correctif est correct et rendra votre travail de code aussi longtemps que time reste assez petit pour que la résolution en virgule flottante soit meilleure que 0,1 (jusqu'à environ 2).

Cela fonctionne parce que le nombre à virgule flottante 0.1*(i+1) calculé à l'itération i est exactement la même que le nombre à virgule flottante calculé comme 0.1*i avec i ayant été incrémentée par un à l'itération suivante, et parce que tant que entiers n et m restent inférieurs à environ 2 , pas deux 0.1*n et 0.1*m sont égaux pour des valeurs différentes de n et m.

La raison est que l'arithmétique en virgule flottante est déterministe. L'opération à virgule flottante 0.1 * n peut produire un résultat contre-intuitif pour certaines valeurs intégrales de n, mais il produit toujours le même résultat pour le même n.


Si en plus il est important pour vous que time est le plus proche possible du quotient mathématique i/10, vous devez calculer time comme i/10.0, et logiquement, calculer available_again comme (i+1)/10.0. Cela continue de fonctionner pour la même raison que ci-dessus et il a la propriété supplémentaire de toujours calculer le nombre à virgule flottante le plus proche du quotient prévu, tandis que 0.1 * i grossit l'erreur de représentation entre le nombre à virgule flottante 0.1 et le rationnel 1/10.

Dans les deux cas, deux valeurs consécutives de time ne seront jamais séparées par le même intervalle. Avec le calcul i/10.0, la valeur à virgule flottante avec float autour du rationnel i/10. Avec 0.1*i, il flottera autour de i * 0.1000000000000000055511151231257827021181583404541015625. Si vous avez la liberté de choisir la fréquence d'échantillonnage, choisissez-le de sorte que le facteur entre i et time soit une puissance de deux (disons 1/64 ou 1/128). Ensuite, vous aurez la propriété supplémentaire que time est calculée exactement et que chaque intervalle de temps est exactement le même.

Questions connexes