mardi, novembre 30, 2010

atof("3.14") == 0

J'avais donné le code suivant à mes étudiants:


int i = atoi(argv[2]); printf("[2] = '%s'\n",argv[2]);
float f = atof(argv[4]); printf("[4] = '%s'\n",argv[4]);
char* s = argv[6];

printf("%i %s %f = %f\n",i,s,f,f+(float)i);

Et l'un d'entre eux vient me trouver en disant que, chez lui, le code affiche toujours "42 plus 0.00000 = 42.0000" quelque soit la valeur donnée à l'argument '-f'. Je récupère le fichier je compile ... ah flûte: pas de devkit SDL sur la machine qui sert à projeter les transparents pendant les répèts. Hop, je commente "#include output.h" je compile je teste ...

Et là, bardaf. pas moyen d'avoir une valeur correcte... Un p'tit coup de manpage Warf. atof renvoie un double là où j'ai utilisé un float ... et printf("%f") suppose aussi la présence d'un double -- un nombre réel à haute précision, et prenant donc 2 fois plus de place ... vu que ça risque de poser des soucis (utilise une valeur tronquée, tout ça), je s/float/double/g tout ça je recompile et ... bin non. Toujours pas.

Tout ça accroupis dans l'auditoire. Je ne me démonte pas, je démare ddd ... qui persiste et signe.
Il aura fallu que je sois de retour sur ma devstation dans mon bureau, à l'aise, pour mettre le doigt sur le bug ... grâce à gcc -Wall, soit-dit en passant. Je vous laisse cogiter, puis passez voir la réponse dans les commentaires: c'est bête comme chou.

3 commentaires:

sylvainulg a dit…

/tmp/tests> gcc -g main.c -Wall
main.c:18: warning: implicit declaration of function 'atoi'
main.c:19: warning: implicit declaration of function 'atof'


eh oui ... je n'avais pas repris <stdlib.h> dans mon fichier main.c, qui était (avec bien d'autres) présent dans le fichier "output.h" faisant aussi appel à SDL. En commentant ce fichier (comme mon étudiant), je me retrouve avec atof non-définie, et pour laquelle le compilateur va utiliser le prototype "fourre-tout": n'importe quels arguments en entrée et un int en sortie.

du coup [[ float f = atof("3.14") ]] est compilé en :
push three_fourteen_string
call atof
;; by convention, integer result are stored in EAX register.
INTEGER_TO_FLOAT(%eax)
STORE_FLOAT_TO_MEM(address_of_f)

la valeur de EAX étant totalement incontrôlée, puisqu'un résultat flottant, lui, est conservé au sommet de la pile du FPU ...

Bien sûr, au moment de "releaser" mon code, je l'avais testé sur ma devstation qui a libsdl1.2-dev installé, et le bug n'était donc pas apparu.

Là dessus, dormez-bien :P

Cyril a dit…

Moralité : des warnings, des warnings et encore des warnings !

sylvainulg a dit…

@cyril : ouaip. Je crois bien que je vais ajouter "setenv CFLAGS -Wall" dans mon .tcshrc pour ce genre de cas où je compile en ligne de commande sans passer par le makefile prévu.