7
votes

Imprimer int comme flotteur en c

J'utilise le compilateur Visual Studio TC pour Little Endian. Ce qui suit est la pièce de code: xxx pré>

la sortie est la suivante: p>

0x3F800000
a = 0.000000
c = 1.000000


10 commentaires

c'est en raison d'un comportement indéfini.


Donner un spécificateur de format de % f et donnant un argument de type INT.


Printf () ne devine pas les types de variables donnés en tant que paramètres. Il suffit de faire confiance aux indicateurs spécifiés par l'utilisateur.


Tout argument float est promu double quand il est passé à une fonction. Pour savoir exactement ce qui se passe, le compilateur produit la production d'assemblage et examine les différences dans le code générées pour chacune des déclarations printf () .


Section 7.21.6 / 9 dit "Si une spécification de conversion n'est pas valide, le comportement n'est pas défini. Si un argument n'est pas le type correct de la spécification de conversion correspondante, le comportement n'est pas défini." . Ceci est pour FPRRTINF mais s'applique également à Printf.


@yoones apparemment, il vérifie ce L1 . Je suis en train de trouver la réponse à la façon dont cela fait. Modifier Je l'ai eu ici .


@Koushik: "comportement indéfini" n'est pas une cause de quelque chose. Un comportement indéfini est un manquant de définition, ce n'est donc qu'un manque de cause pour un comportement souhaité. La cause effective du comportement indésirable dépend des autres choses, telles que des détails de la mise en œuvre non définie par la norme C.


@Ericpostpischil "manque de cause pour un comportement souhaité". Mais le comportement non défini peut également être le manque de cause de comportement indésirable aussi non ?.


@Koushik: Le comportement non défini est toujours non souhaité, au moins à l'extérieur du laboratoire.


* PTR Cause UB en violant la règle d'aliasing stricte (comme toutes les "solutions" conseillent d'écrire * (flotteur *) & a ou similaire)


7 Réponses :


-1
votes

jeter les variables xxx


2 commentaires

Cela ne répond pas à la question, qui est pourquoi cela se comporte comme il le fait, pas comment changer le comportement.


-1 Plus cela ne fonctionne même pas car il jette la valeur d'un flotteur. faire ce qu'il peut * ((flotteur *) & a)



5
votes

J'ai compilé votre code dans GCC et le code généré est suit:

movl    $0x3f800000, %eax
movl    %eax, -4(%ebp)
movl    $1065353216, -8(%ebp)
leal    -4(%ebp), %eax
movl    %eax, -12(%ebp)
movl    -12(%ebp), %eax
movl    (%eax), %eax
movl    %eax, 4(%esp)
movl    $LC1, (%esp)
call    _printf
movl    -8(%ebp), %eax
movl    %eax, 4(%esp)
movl    $LC2, (%esp)
call    _printf
flds    -4(%ebp)
fstpl   4(%esp)
movl    $LC3, (%esp)
call    _printf


4 commentaires

Vous connaissez les registres ponctuels flottants ST0, ST1 (ces travaux de travail comme une pile. Vous appuyez sur (charge) un numéro de la mémoire et de tous les ST (x + 1) = STX et ST0 est réglé sur votre valeur) ...


Désolé d'avoir supprimé mon commentaire, il semble que le vôtre semble "accrocher". Votre réponse montre qu'il existe une pile «régulière» et une «pile à flotteur». Ceci n'est pas seulement faux mais x86 plate-forme spécifique et IMHO n'est pas utile en ce qui concerne la question. Le fait que x86 traite des registres flottants comme une pile n'affecte pas le comportement observé.


Je pense que la réponse à cette question est spécifique à la plate-forme. De cause que le comportement est indéfini. Spécifiquement sur la plate-forme X86, le comportement est causé par le fait que les flotteurs sont stockés dans des registres de flotteurs et tout le reste est en pile.


Nous ont aboyé le mauvais arbre, et medinoc a bien compris: % f pops A (64 bits) double hors de la pile et des impressions cette. Sur les machines low-endian, (32 bits) 0x3f800000 converti en un double (64 bits) ne suffit pas pour apparaître avec la précision par défaut (6 chiffres). Essayez d'augmenter la précision, utilisez un type 64 bits pour A ou (et c'est ce que convaincu moi ) appelle printf ("% f \ n", 0 , a) pour appuyer sur la valeur de A à l'endroit où il importe % f . Frères dans la défaite, +1 à Medinoc. (C'est toujours UB, bien sûr.)



5
votes

comme -wall états: AVERTISSEMENT: le format '% F' s'attend à ce que le type 'double', mais l'argument 2 a le type 'int' . Ceci est un comportement indéfini, comme expliqué également plus en détail ici .

Si une spécification de conversion n'est pas valide, le comportement est indéfini. Si un argument n'est pas le type correct pour la spécification de couverture correspondante, le comportement est indéfini.

Alors, ce que vous voyez ici, c'est ce que le constructeur de compilateur a décidé de se produire, ce qui peut être n'importe quoi.


4 commentaires

Les compilateurs peuvent traiter Printf spécialement? Les compilateurs savent-ils comment traiter avec Printf?


Non mais ils définissent ce qui se passe en cas de conversion de type invalide.


Mais l'avertissement parle de la nullité de l'argument la vérifie contre le «spécificateur de format»?


ah oui vous avez raison et j'ai eu la raison pour laquelle aussi L1 . +1



-1
votes
int a= 0x3F800000;
printf("\na = %f", *(float*)&a);//1.0

0 commentaires

6
votes

Mes puissances psychiques me disent que le commentaire de Adam Liss est la bonne réponse: float Les arguments sont promus à double , donc la fonction printf () s'attend à ce que cela se produise: il attend une valeur de 64 bits sur la pile, mais obtient 32 bits plus des données à ordures qui se produisent de zéro.

Si vous augmentez la précision de l'affichage, l'affichage doit être quelque chose comme A = 0.00000000001 .

Cela signifie également que cela devrait fonctionner: xxx


2 commentaires

Medinoc merci beaucoup. Cela explique tout.


+1. J'ai passé une demi-heure à essayer de vous tromper. Le démontage a montré que double C est créé dans un registre FPU immédiatement (par opposition à float c ), donc je soupçonnai à "corriger" votre résultat pour la mauvaise raison. Mais même enlever le double totalement obtenu la sortie observée pour A . Ensuite, j'ai soupçonné la manipulation spéciale pour les entiers 64 bits par GCC ... mais quand un fichier printf ("% f \ n", 0x0, 0x3FF0000) a donné 1.0 sans aucun < Code> Double ou Long Long Long impliqué, je devais admettre la défaite. En regardant les registres FPU était une chasse à l'oie sauvage.



2
votes

Dans toute mise en œuvre C, il existe des règles sur la manière dont les paramètres sont transmis aux fonctions. Ces règles peuvent dire que les paramètres de certains types sont passés dans certains registres (par exemple, des types d'entiers dans des registres généraux et des types de points flottants dans des registres de points flottants distincts), que de grands arguments (tels que des structures avec de nombreux éléments) sont transmis sur le pile ou par un pointeur sur une copie de la structure, etc.

À l'intérieur d'une fonction appelée, la fonction recherche le paramètre qu'il attend dans les endroits que les règles spécifient. Lorsque vous passez un entier dans un argument sur printf mais transmettez-le % f dans la chaîne de format, vous mettez un entier quelque part, mais indiquez printf chercher un flotteur (qui a été promu à un double). Si les règles de votre implication C spécifient que l'argument entier est passé au même endroit qu'un argument double, alors printf trouvera les bits de votre entier, mais il les interprétera comme un double. D'autre part, si les règles de votre implication C spécifient différents endroits pour les arguments, les bits de votre entier ne sont pas où printf recherche le double. Donc printf trouve des autres bits qui n'ont rien à voir avec votre entier.

En outre, de nombreuses implémentations C ont des types int 32 bits et 64 bits double types. Le spécificateur % F est destiné à imprimer un double, pas un flotteur et la valeur de flotteur que vous passez est convertie en double avant d'appeler la fonction. Donc, même si printf trouve les bits de votre entier, il n'y a que 32 bits là-bas, mais printf utilise 64. donc le double qui est Imprimé est composé de 32 bits que vous avez passé et 32 ​​autres bits, et ce n'est pas la valeur que vous avez destinée à imprimer.

C'est pourquoi les spécificateurs de format que vous utilisez doivent être correspondent aux arguments que vous passez.


0 commentaires

1
votes

Je rencontre le problème similaire, et enfin, je développe un moyen de résoudre le problème, je ne sais pas si c'est ce que vous voulez. Le point clé est que: vous devez passer un flotteur au lieu d'un entier.

0x3F800000

a = 0.000000
c = 1.000000
fp = 1.000000


4 commentaires

Cela a toujours un comportement non défini, tous les mêmes problèmes initiaux restent que discutés dans les réponses antérieures et les commentaires précédents. Ce que vous avez fait est tombé sur une combinaison d'un alignement planétaire qui semble fonctionner à l'heure actuelle, sur un système avec une version compilatrice et une combinaison de paramètres.


Je ne suis pas aussi familier avec C, pouvez-vous expliquer quelle ligne de code contient légèrement un comportement indéfini?


Lignes 13,14,16 faire; et les lignes 11 et 12 pourraient faire. Je ne veux pas répéter ce qui a déjà été dit, alors lisez d'autres commentaires.


vous avez raison . Je viens de comprendre ce que j'ai fait, c'est exactement la même chose que les autres ont discuté auparavant.