Conversion implicite d'unsigned

Le
Rémi Moyen
Bonjour,

J'ai remarqué un truc qui me semble un peu bizarre en manipulant des
unsigned int et des int. Je suis tombé sur le problème dans un code C+
+, mais un programme de test en C me donne la même chose, et j'ai le
même comportement sur 3 compilateurs différents, donc il doit
définitivement y avoir une logique derrière tout ça.

Voilà le code :
#include <stdio.h>
int main(int argc, char** argv) {
unsigned int i;
int a, b, c;

i = 100;
printf("%u", i);

a = -1 * i;
printf("%d", a);

/* les parentheses sont justes pour expliciter l'ordre, mais
elles sont inutiles */
b = (-1 * i) * 2;
printf("%d", b);

c = (-1 * i) / 2;
printf("%d", c);

return 0;
}

Et le résultat est :
100
-100
-200
2147483598

Je n'arrive pas à suivre toutes les conversions implicites qui ont
lieu. Au passage, je précise que j'ai testé en définissant des
variables pour mes constantes -1 et 2 (de type int ou unsigned),
histoire de forcer le type, et j'ai toujours le même comportement.
Pour le premier print (i), rien à dire évidemment. Dans le deuxième
cas, je suppose que i est casté en int avant de multiplier par -1 ?
Pour le troisième cas, idem ? Dans le 4ème, j'obtiens (2^31 - 100) /
2. Pourquoi ?

Que (unsigned int)(-1) me donne 4 294 967 295 ( = 2^32 - 1), OK, je
n'ai pas de problème avec l'over/underflow. Mais pourquoi multiplier
un unsigned par -1 me donne ce résultat ?

Et pourquoi j'ai ce résultat uniquement si je divise après (c), et pas
si je multiplie (b) ou que je me contente de stocker le résultat (a) ?

Merci d'avance !
--
Rémi Moyen
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
Marc Boyer
Le #1159794
J'ai remarqué un truc qui me semble un peu bizarre en manipulant des
unsigned int et des int.


Le truc dangeureux par nature.

Je suis tombé sur le problème dans un code C+
+, mais un programme de test en C me donne la même chose, et j'ai le
même comportement sur 3 compilateurs différents, donc il doit
définitivement y avoir une logique derrière tout ça.


Et oui.
Disons pour faire simple que "souvent", quand il y a un signé et
un non signé dans la même expression, le signé est convertit en
non signé (et non l'inverse...).

Voilà le code :
#include int main(int argc, char** argv) {
unsigned int i;
int a, b, c;

i = 100;
printf("%un", i);

a = -1 * i;


Si on décompose ce code, on a un int et un unsigned. L'int
est convertit en unsigned, donc, il vaut (UINT_MAX-1). Ensuite,
on multiplie cet entier par 100, et on obtient
((UINT_MAX-1)*100) % UINT_MAX
puis on reconvertit le tout en int (ce qui doit être une conversion
dépendante de l'encodage "implementation-defined"), et, sur ta
plateforme en complément à 2, ça doit donner -100.

Je n'arrive pas à suivre toutes les conversions implicites qui ont
lieu.


Ben, moi j'en sais juste assez pour savoir qu'il faut faire
attention. Dans 6.3.1.8/1, il y a le paragraphe qui te concerne:
"Otherwise [ie un signé et un non signé] if the operand that has
unsigned integer type has rank greater or equal to the rank of
the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with
unsigned integer type."

Et sans entrer dans les détails int et unsigned int ont le même rank.


Pour le premier print (i), rien à dire évidemment. Dans le deuxième
cas, je suppose que i est casté en int avant de multiplier par -1 ?


Perdu, c'est le contraire.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Rémi Moyen
Le #1159793
On Feb 29, 12:55 pm, Marc Boyer wrote:
J'ai remarqué un truc qui me semble un peu bizarre en manipulant des
unsigned int et des int.


  Le truc dangeureux par nature.


Oui, je sais... Pour tout dire, je n'ai pas l'intention de laisser un
truc de ce genre dans mon code, mais comme je suis tombé là-dessus par
hasard, j'essaie de comprendre la raison du pourquoi...

  Disons pour faire simple que "souvent", quand il y a un signé et
un non signé dans la même expression, le signé est convertit en
non signé (et non l'inverse...).


OK.

        unsigned int i;
        int a, b, c;

        i = 100;
        printf("%un", i);

        a = -1 * i;


  Si on décompose ce code, on a un int et un unsigned. L'int
est convertit en unsigned, donc, il vaut (UINT_MAX-1). Ensuite,
on multiplie cet entier par 100, et on obtient
    ((UINT_MAX-1)*100) % UINT_MAX
puis on reconvertit le tout en int (ce qui doit être une conversion
dépendante de l'encodage "implementation-defined"), et, sur ta
plateforme en complément à 2, ça doit donner -100.


Je vois, en effet (ceci dit, j'ai quand même testé sur un PC Linux
2.4, un PC Linux 2.6 et une Sun -- dont l'endianness est différente
des deux autres --, donc les implémentations sont quand même vachement
similaires, mais peu importe, et je comprends que rien n'empêche que
ce ne soit pas le cas sur une 4ème plateforme...).

Pour le premier print (i), rien à dire évidemment. Dans le deuxièm e
cas, je suppose que i est casté en int avant de multiplier par -1 ?


  Perdu, c'est le contraire.


En effet, en regardant les choses dans ce sens-là, je comprends ce qui
se passe (j'ai fait les calculs à la main avec des char/unsigned char
et i et je retombe bien sur le résultat que me donne mon programme
en suivant ton raisonnement).

Merci !
--
Rémi Moyen


Marc Boyer
Le #1159792
On 2008-02-29, Rémi Moyen
  Si on décompose ce code, on a un int et un unsigned. L'int
est convertit en unsigned, donc, il vaut (UINT_MAX-1). Ensuite,
on multiplie cet entier par 100, et on obtient
    ((UINT_MAX-1)*100) % UINT_MAX
puis on reconvertit le tout en int (ce qui doit être une conversion
dépendante de l'encodage "implementation-defined"), et, sur ta
plateforme en complément à 2, ça doit donner -100.


Je vois, en effet (ceci dit, j'ai quand même testé sur un PC Linux
2.4, un PC Linux 2.6 et une Sun -- dont l'endianness est différente
des deux autres --, donc les implémentations sont quand même vachement
similaires, mais peu importe, et je comprends que rien n'empêche que
ce ne soit pas le cas sur une 4ème plateforme...).


Ici, c'est surtout du à l'encodage en complément à 2. Bon, ok,
pour trouver une machine qui soit pas en complément à deux de nos
jours, faut chercher un peu...

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Jean-Marc Bourguet
Le #1160341
"Rémi Moyen"
  Si on décompose ce code, on a un int et un unsigned. L'int
est convertit en unsigned, donc, il vaut (UINT_MAX-1). Ensuite,
on multiplie cet entier par 100, et on obtient
    ((UINT_MAX-1)*100) % UINT_MAX puis on reconvertit le tout en int
Non,


((UINT_MAX-1)*100) % (UINT_MAX+1)
ou
((UINT_MAX-1)*100) & UINT_MAX

(ce qui doit être une conversion dépendante de l'encodage
"implementation-defined"), et, sur ta plateforme en complément à 2, ça
doit donner -100.


Je vois, en effet (ceci dit, j'ai quand même testé sur un PC Linux
2.4, un PC Linux 2.6 et une Sun -- dont l'endianness est différente
des deux autres --, donc les implémentations sont quand même vachement
similaires, mais peu importe, et je comprends que rien n'empêche que
ce ne soit pas le cas sur une 4ème plateforme...).


La boutidude n'a pas d'influence la dessus (elle n'intervient que quand tu
interpretes de la memoire comme etant composees d'elements de tailles
differentes). Il est vraisemblable que toutes les machines dont tu
disposes soient en complement a 2, les autres sont rares.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Marc Boyer
Le #1162570
On 2008-02-29, Jean-Marc Bourguet
"Rémi Moyen"
  Si on décompose ce code, on a un int et un unsigned. L'int
est convertit en unsigned, donc, il vaut (UINT_MAX-1). Ensuite,
on multiplie cet entier par 100, et on obtient
    ((UINT_MAX-1)*100) % UINT_MAX puis on reconvertit le tout en int
Non,


((UINT_MAX-1)*100) % (UINT_MAX+1)


Oui, bien sûr, pardon.
Ceci dit, on est vendredi ;-)

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)



Antoine Leca
Le #1183173
En news:,
Marc Boyer va escriure:
unsigned int i;
int a, b, c;

i = 100;
a = -1 * i;


Si on décompose ce code, on a un int et un unsigned. L'int
est convertit en unsigned, donc, il vaut (UINT_MAX-1).


UINT_MAX

Ensuite, on multiplie cet entier par 100, et on obtient
((UINT_MAX-1)*100) % UINT_MAX
( UINT_MAX *100) %(UINT_MAX+1)



Antoine


Publicité
Poster une réponse
Anonyme