Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Conversion implicite d'unsigned

6 réponses
Avatar
Rémi Moyen
Bonjour,

J'ai remarqu=E9 un truc qui me semble un peu bizarre en manipulant des
unsigned int et des int. Je suis tomb=E9 sur le probl=E8me dans un code C+
+, mais un programme de test en C me donne la m=EAme chose, et j'ai le
m=EAme comportement sur 3 compilateurs diff=E9rents, donc il doit
d=E9finitivement y avoir une logique derri=E8re tout =E7a.

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

i =3D 100;
printf("%u\n", i);

a =3D -1 * i;
printf("%d\n", a);

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

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

return 0;
}

Et le r=E9sultat est :
100
-100
-200
2147483598

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

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

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

Merci d'avance !
--
R=E9mi Moyen

6 réponses

Avatar
Marc Boyer
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 <stdio.h>
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)

Avatar
Rémi Moyen
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


Avatar
Marc Boyer
On 2008-02-29, Rémi Moyen wrote:
  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)


Avatar
Jean-Marc Bourguet
"Rémi Moyen" writes:

  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


Avatar
Marc Boyer
On 2008-02-29, Jean-Marc Bourguet wrote:
"Rémi Moyen" writes:

  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)



Avatar
Antoine Leca
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