pow () converti en entier, résultat inattendu

J’ai quelques problèmes à utiliser une conversion entière pour la fonction pow() dans le langage de programmation C. Le compilateur que j’utilise est le compilateur Tiny C (version 0.9.24 de tcc) pour la plate-forme Windows. Lors de l’exécution du code suivant, le résultat inattendu 100, 99 :

 #include  #include  int main(void) { printf("%d, ", (int) pow(10, 2)); printf("%d", (int) pow(10, 2)); return 0; } 

Cependant, sur ce compilateur en ligne, la sortie est comme prévu: 100, 100 . Je ne sais pas ce qui cause ce comportement. Des pensées? Erreur de programmation de moi, bug du compilateur?

Vous avez trouvé un bug dans tcc. Merci pour ça. Le correctif vient d’être validé dans le référentiel. Il sera inclus dans la prochaine version, mais cela peut prendre un certain temps. Vous pouvez bien sûr extraire la source et la construire vous-même. Le patch est là

http://repo.or.cz/w/tinycc.git/commitdiff/73faaea227a53e365dd75f1dba7a5071c7b5e541

Une enquête dans le code de assembly. (OllyDbg)

 #include  #include  int main(void) { int x1 = (int) pow(10, 2); int x2 = (int) pow(10, 2); printf("%d %d", x1, x2); return 0; } 

La section d’assemblage associée:

 FLD QWORD PTR DS:[402000] // Loads 2.0 onto stack SUB ESP,8 FSTP QWORD PTR SS:[ESP] FLD QWORD PTR DS:[402008] // Loads 10.0 onto stack SUB ESP,8 FSTP QWORD PTR SS:[ESP] CALL  // Calls pow API // Returned value 100.00000000000000000 ... FLDCW WORD PTR DS:[402042] // OH! LOOK AT HERE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... FLD QWORD PTR DS:[402010] // Loads 2.0 onto stack SUB ESP,8 FSTP QWORD PTR SS:[ESP] FLD QWORD PTR DS:[402018] // Loads 10.0 onto stack SUB ESP,8 FSTP QWORD PTR SS:[ESP] CALL  // Calls pow API again // Returned value 99.999999999999999990 

Le code généré pour deux appels est le même, mais les sorties sont différentes. Je ne sais pas pourquoi TCC a mis FLDCW là-bas. Mais la raison principale de deux valeurs différentes est cette ligne.

Avant cette ligne, les bits de contrôle de précision Mantissa arrondis sont de 53 bits (10), mais après exécution de cette ligne (le contrôle du registre FPU est chargé), il est réglé sur 64 bits (11). Par contre, le contrôle d’arrondi est le plus proche , le résultat est donc 99.999999999999999990. Lire la suite…

entrez la description de l'image ici


Solution:

Après avoir utilisé (int) pour convertir un float en int , attendez-vous à cette erreur numérique, car cette conversion tronque les valeurs comsockets entre [0, 1) à zéro.

Supposons que le 10 2 est 99,9999999999. Après cette dissortingbution, le résultat est 99.

Essayez d’arrondir le résultat avant de le convertir en entier, par exemple:

 printf("%d", (int) (floor(pow(10, 2) + 0.5)) ); 

Il semble que la méthode d’arrondi puisse changer, ce qui nécessite une instruction d’instruction ASM pour réinitialiser la FPU. Dans FreeBASIC sous Windows, je reçois 99.9999 même au premier essai et je pense donc que pour vous après le premier essai, ce serait un 99.9999 cohérent. (Mais j’appelle ce comportement indéfini, bien plus qu’un bogue dans pow() du runtime C pow() .

Donc, mon conseil n’est pas de faire la conversion avec arrondir. Pour éviter de tels problèmes, utilisez, par exemple:

int x1 = (int)(pow(10, 2)+.5);