web-dev-qa-db-fra.com

Convertir un entier non signé en un entier signé C

J'essaie de convertir 65529 d'un unsigned int vers un int signé. J'ai essayé de faire un casting comme celui-ci:

unsigned int x = 65529;
int y = (int) x;

Mais y renvoie toujours 65529 alors qu'il devrait retourner -7. Pourquoi donc?

17
darksky

Il semble que vous attendiez int et unsigned int pour être un entier de 16 bits. Ce n'est apparemment pas le cas. Il s'agit très probablement d'un entier 32 bits, ce qui est suffisamment grand pour éviter le bouclage que vous attendez.

Notez qu'il n'y a pas de moyen entièrement compatible C de le faire car la conversion entre signé/non signé pour les valeurs hors plage est définie par l'implémentation. Mais cela fonctionnera toujours dans la plupart des cas:

unsigned int x = 65529;
int y = (short) x;      //  If short is a 16-bit integer.

ou bien:

unsigned int x = 65529;
int y = (int16_t) x;    //  This is defined in <stdint.h>
32
Mysticial

Je sais que c'est une vieille question, mais c'est une bonne question, alors qu'en est-il?

unsigned short int x = 65529U;
short int y = *(short int*)&x;

printf("%d\n", y);
6
Subsentient

@Mysticial l'a compris. Un court est généralement de 16 bits et illustrera la réponse:

int main()  
{
    unsigned int x = 65529;
    int y = (int) x;
    printf("%d\n", y);

    unsigned short z = 65529;
    short zz = (short)z;
    printf("%d\n", zz);
}

65529
-7
Press any key to continue . . .

Regardons donc la décimale 65529. Il peut être représenté par FFF9h en hexadécimal. On peut aussi représenter cela en binaire comme:

11111111 11111001

Lorsque nous déclarons short zz = 65529;, le compilateur interprète 65529 comme une valeur signée. En notation à deux compléments, le bit supérieur signifie si une valeur signée est positive ou négative. Dans ce cas, vous pouvez voir que le bit supérieur est un 1, il est donc traité comme un nombre négatif. C'est pourquoi il imprime -7.

Pour un unsigned short, nous ne nous soucions pas du signe car c'est unsigned. Ainsi, lorsque nous l'imprimons à l'aide de %d, nous utilisons les 16 bits, il est donc interprété comme 65529.

4
JoeFish

Pour comprendre pourquoi, vous devez savoir que le CPU représente des nombres signés en utilisant le complément à deux (peut-être pas tous, mais beaucoup).

    byte n = 1; //0000 0001 =  1
    n = ~n + 1; //1111 1110 + 0000 0001 = 1111 1111 = -1

Et aussi, que le type int et int non signé peuvent être de tailles différentes selon votre CPU. Lorsque vous faites des choses spécifiques comme ceci:

   #include <stdint.h>
   int8_t ibyte;
   uint8_t ubyte;
   int16_t iword;
   //......

La représentation des valeurs 65529u et -7 est identique pour les entiers 16 bits. Seule l'interprétation des bits est différente.

Pour des valeurs plus importantes et ces valeurs, vous devez signer étendre; une façon est avec des opérations logiques

int y = (int )(x | 0xffff0000u); // assumes 16 to 32 extension, x is > 32767

Si la vitesse n'est pas un problème ou si la division est rapide sur votre processeur,

int y = ((int ) (x * 65536u)) / 65536;

La multiplication décale vers la gauche de 16 bits (là encore, en supposant une extension de 16 à 32), et la division décale vers la droite en maintenant le signe.

1
Doug Currie

Vous vous attendez à ce que votre type int ait une largeur de 16 bits, auquel cas vous obtiendrez en effet une valeur négative. Mais il est très probablement large de 32 bits, donc un int signé peut très bien représenter 65529. Vous pouvez le vérifier en imprimant sizeof(int).

0
Szabolcs

Pour répondre à la question posée dans le commentaire ci-dessus - essayez quelque chose comme ceci:

unsigned short int x = 65529U;
short int y = (short int)x;

printf("%d\n", y);

ou

unsigned short int x = 65529U;
short int y = 0;

memcpy(&y, &x, sizeof(short int);
printf("%d\n", y);
0
Throwback1986

Étant donné que la conversion de valeurs non signées sert à représenter des nombres positifs, la conversion peut être effectuée en définissant le bit le plus significatif sur 0. Par conséquent, un programme n'interprétera pas cela comme une valeur de complément à deux. Une mise en garde est que cela perdra des informations pour les nombres proches du maximum du type non signé.

template <typename TUnsigned, typename TSinged>
TSinged UnsignedToSigned(TUnsigned val)
{
    return val & ~(1 << ((sizeof(TUnsigned) * 8) - 1));
}
0
amilamad