Test d'égalité entier - flottant

Le
Fane
Bonjour,

J'ai un système qui me donne des informations suivant un temps en
secondes (au format double) dont je ne maîtrise pas la fréquence.
Je veux n'afficher ces informations que toutes les 10 secondes.
Cela revient donc à faire un test pour savoir si le temps courant est
un multiple de 10.

Le code de test ci-dessous m'a l'air de fonctionner.
Maintenant, est-ce la bonne méthode ?
Peux-t-on faire mieux ?


#include <math.h>
#include <stdio.h>

int main(void)
{
int i = 0;
int n = 36001;
double step = 0.1;
double seconds = 0.0;

for (; i<n; i++)
{
double sf; /* floored seconds */
double sc; /* ceiled seconds */
double sd; /* seconds that must be displayed */
double discr = 10.0;
double precision = 1e-3;

sf = floor(seconds / discr) * discr;
sc = ceil(seconds / discr) * discr;
if (fabs(seconds - sf) < precision)
sd = sf;
else
sd = sc;

if (fabs(seconds - sd) <= precision)
{
printf("seconds=%gt displayed at=%g", seconds, sd);
}

seconds += step;
}
}



Bonne journée,
Fane
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Alain Montfranc
Le #18752991
Fane a écrit
Bonjour,

J'ai un système qui me donne des informations suivant un temps en
secondes (au format double) dont je ne maîtrise pas la fréquence.
Je veux n'afficher ces informations que toutes les 10 secondes.
Cela revient donc à faire un test pour savoir si le temps courant est
un multiple de 10.

Le code de test ci-dessous m'a l'air de fonctionner.
Maintenant, est-ce la bonne méthode ?
Peux-t-on faire mieux ?


#include #include
int main(void)
{
int i = 0;
int n = 36001;
double step = 0.1;
double seconds = 0.0;

for (; i<n; i++)
{
double sf; /* floored seconds */
double sc; /* ceiled seconds */
double sd; /* seconds that must be displayed */
double discr = 10.0;
double precision = 1e-3;

sf = floor(seconds / discr) * discr;
sc = ceil(seconds / discr) * discr;
if (fabs(seconds - sf) < precision)
sd = sf;
else
sd = sc;

if (fabs(seconds - sd) <= precision)
{
printf("seconds=%gt displayed at=%gn", seconds, sd);
}

seconds += step;
}
}



Bonne journée,
Fane




je ferais plutot un truc plus simple :
- avoir une variabl "last" qui contient le timestamp du dernier
affichage
- si (timestamp courant) >= (last + 10.0) => afficher et mettre a jour
last
Antoine Leca
Le #18771511
Le 24/02/2009 16:23, Fane
J'ai un système qui me donne des informations suivant un temps en
secondes (au format double) dont je ne maîtrise pas la fréquence.
Je veux n'afficher ces informations que toutes les 10 secondes.
Cela revient donc à faire un test pour savoir si le temps courant est
un multiple de 10.



Non. Si tu supposes que tu disposes d'une base de temps précise, tu
devrais comprendre que tu ne peux pas être sûr de regarder l'horloge
EXACTEMENT au moment l'aiguille des minutes est sur un multiple de 10 ET
l'aiguille des secondes sur 0 ET l'aiguille des nanosecondes sur 0 !
Déjà dans la pratique, pour te synchroniser avec ta montre à la seconde
près, tu vas attendre qu'il soit disons 19:09:50 (environ), et tu vas
arrêter de faire quoique que ce soit d'autres jusqu'à ce que l'aiguille
des secondes passe sur le 0.

En informatique c'est pareil, lorsque tu approches du moment qui va bien...
if( prochain_top-temps_actuel()>10.0 ) va_faire_autre_chose();
tu te concentres...
while( temps_actuel()<prochain_top )
fait_juste_un_truc_rapide();
et quand c'est bon tu affiches :
affiche();
et on attend le prochain top !
prochain_top += 10.0;
/* ou encore prochain_top = temps_actuel()+10.0; */


En réalité les deux tests sont équivalents (donc on peut en supprimer
un), mais le comportement n'est pas le même : le premier est un test sur
interruption ou polling, le second est une attente active.


Et à part cela, OEQLC?


Antoine
Fane
Le #18777881
On 26 fév, 19:25, Antoine Leca
Le 24/02/2009 16:23, Fane
> J'ai un système qui me donne des informations suivant un temps en
> secondes (au format double) dont je ne maîtrise pas la fréquence.
> Je veux n'afficher ces informations que toutes les 10 secondes.
> Cela revient donc à faire un test pour savoir si le temps courant est
> un multiple de 10.

Non. Si tu supposes que tu disposes d'une base de temps précise, tu
devrais comprendre que tu ne peux pas être sûr de regarder l'horloge
EXACTEMENT au moment l'aiguille des minutes est sur un multiple de 10 ET
l'aiguille des secondes sur 0 ET l'aiguille des nanosecondes sur 0 !
Déjà dans la pratique, pour te synchroniser avec ta montre à la sec onde
près, tu vas attendre qu'il soit disons 19:09:50 (environ), et tu vas
arrêter de faire quoique que ce soit d'autres jusqu'à ce que l'aiguil le
des secondes passe sur le 0.



Justement, ce n'est pas du tout ce que je veux faire.
C'est l'objet du test avec une précision à e-3 dans mon code.

De toute façon, ce n'était surement pas la bonne méthode.
Il vaut mieux afficher le premier pas de temps dépassant un multiple
de 10.

[...]


Et à part cela, OEQLC?



Ben, je savais faire en java ou en scheme. J'ai tenté un truc en C et
je voulais savoir si je pouvais le programmer comme ça.
Désolé, si tu penses que c'était HS.

Fane.
Antoine Leca
Le #18780291
Le 27/02/2009 12:24, Fane écrivit :
Et à part cela, OEQLC?



Ben, je savais faire en java ou en scheme. J'ai tenté un truc en C et
je voulais savoir si je pouvais le programmer comme ça.
Désolé, si tu penses que c'était HS.



En fait, l'introduction (à propos des horloges et tout cela) de ton
premier message a suscité deux réponses sur le mode « il ne faut pas
faire comme cela » concernant en fait le piège de faire une opération de
comparaison entre une donnée un tant soit peu aléatoire et une constante
(le fait que ce soit des float ne rentre pas en ligne de compte, si le
temps est mesuré en ms dans un entier et que tu compares à un multiple
de 600000 tu as exactement le même souci). Ce problème n'a rien à voir
avec le langage C en soit, même si c'est un contexte où on le rencontre
facilement à cause de l'utilisation des float en C, d'où les réponses «
automatiques ».

En fait ta vraie question était, comment faire en C une opération sur
des flottants. Et évidemment, ce n'est absolument pas hors sujet sur ce
groupe, c'est seulement inutile dans le contexte annoncé ;-).


: Cela revient donc à faire un test pour savoir si le temps courant est
: un multiple de 10.
:
: Le code de test ci-dessous m'a l'air de fonctionner.
: Maintenant, est-ce la bonne méthode ?
: Peux-t-on faire mieux ?

Je ne sais pas si c'est mieux (il faudrait déjà définir l'échelle de
jugement pour ce faire), mais je trouve cela un peu compliqué.

Savoir si une valeur double V est un multiple de X :
int est_multiple(double v, double x) {
return !fmod(v,x);
}

Évidemment, codé aussi bêtement, cela ne va pas marcher pour des
problèmes d'arrondis. Si V est légèrement supérieur à nX, donc si le
résultat de fmod est inférieur à l'epsilon E, cela vaut aussi ; d'où
int est_presque_multiple(double v, double x, double e) {
return fmod(v,x) < e;
}

Mais bien sûr, le problème existe aussi si V est légèrement inférieur...
ce qui revient à dire que V+E soit légèrement supérieur à nX, d'où
int est_proche_multiple(double v, double x, double e) {
return fmod(v+e,x) < e*2.0;
}

[ Régler les cas où V, X ou E sont négatifs ou nuls est laissé à
l'appréciation du lecteur ; pour ma part j'estime que cela alourdira
notablement le code sans avoir un intérêt pratique exceptionnel. ]



Avec l'environnement de test que tu avais mis (et en supposant le
générateur de nombres aléatoires proposé par la norme), cela donne

#include
int est_proche_multiple(double valeur, double base, double epsilon)
{
return fmod(valeur+epsilon, base) < 2.0*epsilon;
}

#include #include
int main(void)
{
int i = 0, j = 0;
int n = 50000;
double step = 0.1/23456.0;
double seconds = 0.0;
double ref[] {0,0,260,300,310,330,510,850,1020,1050,2280,2490,3390,-1};


for (; i<n; i++)
{
double discr = 10.0;
double precision = 1e-3;

if ( est_proche_multiple(seconds, discr, precision) )
{
printf("secondes=%-12ft affiché à %gn",
seconds, floor(seconds+.5) );
if( ref[j]==floor(seconds+.5) ) ++j;
}

seconds += step*rand();
}

return j= ? 0 : EXIT_FAILURE;
}


Antoine
Publicité
Poster une réponse
Anonyme