2010-09-21 4 views
4

J'aidais quelqu'un à faire ses devoirs et j'ai été confronté à ce problème étrange. Le problème est d'écrire une fonction qui inverse l'ordre des octets d'un entier signé (C'est ainsi que la fonction a été spécifiée de toute façon), et c'est la solution que je suis venu avec:Décalage à droite signé = résultat étrange?

int reverse(int x) 
{ 
    int reversed = 0; 

    reversed = (x & (0xFF << 24)) >> 24; 
    reversed |= (x & (0xFF << 16)) >> 8; 
    reversed |= (x & (0xFF << 8)) << 8; 
    reversed |= (x & 0xFF) << 24; 

    return reversed; 
} 

Si vous passez 0xFF000000 à cette fonction, la première affectation aura pour résultat 0xFFFFFFFF. Je ne comprends pas vraiment ce qui se passe, mais je sais que cela a quelque chose à voir avec les conversions entre les signataires et les non signés, ou quelque chose comme ça.

Si j'applique ul à 0xFF cela fonctionne très bien, ce que je suppose est parce qu'il est forcé à non signé puis converti en signé ou quelque chose dans cette direction. Le code résultant change également; sans le spécificateur ul, il utilise sar (arithmétique de décalage à droite), mais comme non signé, il utilise shr comme prévu.

J'apprécierais vraiment si quelqu'un pourrait éclaircir cela pour moi. Je suis supposé savoir ce genre de choses, et je pensais que c'était le cas, mais je ne suis pas vraiment sûr de ce qui se passe ici.

Merci d'avance!

+3

* Pourquoi * vous déclarez '' reversed' comme unsigned'? –

+3

"J'aidais quelqu'un à faire ses devoirs" .... alors s'il vous plaît retag comme "devoirs". –

+0

Konrad: Ah, c'est une erreur. Je le réparerai. J'essayais différentes choses pour comprendre ce qui se passait. – identity

Répondre

12

Depuis x est un signé quantité, le résultat de (x & (0xFF << 24)) est 0xFF000000 qui est également signé et donc un nombre négatif depuis le haut (signe) bit est défini. L'opérateur >> sur int (une valeur signée) exécute extension de signe (Edit: bien que ce comportement soit indéfini et spécifique à l'implémentation) et propage la valeur de bit de signe 1 lorsque la valeur est décalée vers la droite.

Vous devez réécrire la fonction comme suit pour travailler exclusivement sur des valeurs non signées:

unsigned reverse(unsigned x) 
{ 
    unsigned int reversed = 0; 

    reversed = (x & (0xFF << 24)) >> 24; 
    reversed |= (x & (0xFF << 16)) >> 8; 
    reversed |= (x & (0xFF << 8)) << 8; 
    reversed |= (x & 0xFF) << 24; 

    return reversed; 
} 
+5

Je souhaite que les commentaires puissent être dépréciés. Les bits 31-24 s'échangent avec 7-0 (shift 24), 23-16 swap avec 15-8 (shift 8, pas 16). Rétabli à l'algorithme correct. –

+0

Désolé pour cela ... –

+4

Il convient de mentionner que le décalage vers la gauche dans le bit de signe a un comportement ** indéfini ** et le décalage de droite suivant (avec extension de signe) est un comportement ** spécifique à l'implémentation **. Pour faire une histoire courte, ** vous devriez utiliser des types non signés ici **. –

1

x est signé, de sorte que le bit est utilisé pour le signe. 0xFF000000 signifie "négatif 0x7F000000". Lorsque vous effectuez le décalage, le résultat est "Signe étendu": le chiffre binaire ajouté à gauche pour remplacer l'ancien MSB déplacé vers la droite est toujours le même que le signe de la valeur. Alors

0xFF000000 >> 1 == 0xFF800000 
0xFF000000 >> 2 == 0xFFC00000 
0xFF000000 >> 3 == 0xFFE00000 
0xFF000000 >> 4 == 0xFFF00000 

Si la valeur étant décalée est non signé, ou si le décalage est vers la gauche, le nouveau bit serait 0. Il est seulement en droit-quarts des valeurs signées qui extension de signe entrent en jeu.

+3

0xFF000000! = -0x7F000000 (au moins, en règle générale, les processeurs qui utilisent des bits de signe indépendants pour les entiers plutôt que de compléter la représentation doivent être <1% du marché). Si c'était le cas, l'extension de signe ne serait pas nécessaire, la préservation du signe serait. –

+0

Pour mémoire, sur les machines à complément à 2 bits 32 bits comme celle que l'OP utilise clairement, '0xFF000000' est' -0x01000000'. En complément de 2, '-x' est équivalent à' (~ x) + 1'. C'est-à-dire, compléter chaque bit, puis ajouter 1 au résultat. – RBerteig

+0

Vrai, mais cela ne change pas de façon significative ma réponse, mais traîne juste dans un détail extranous sans rapport avec la question. Si j'avais fait cela, cela aurait rendu la réponse plus simple et plus confuse. –

7

À partir de vos résultats, nous pouvons déduire que vous êtes sur une machine 32 bits.

(x & (0xFF << 24)) >> 24 

Dans cette expression 0xFF est un int, donc 0xFF << 24 est aussi un int, comme x.

Lorsque vous effectuez une opération de bit & entre deux int, le résultat est aussi un int et dans ce cas, la valeur est 0xFF000000 qui, sur une machine 32 bits signifie que le bit de signe est défini, vous avez donc un nombre négatif.

Le résultat de l'exécution d'un décalage vers la droite sur un objet de type signé avec une valeur négative est défini par l'implémentation.Dans votre cas, le décalage arithmétique est conservé.

Si vous effectuez un décalage vers la droite d'un type non signé, vous obtiendrez les résultats attendus pour une fonction d'inversion d'octet. Vous pouvez réaliser cela en faisant opérande l'un ou l'autre des opérandes bit à bit & un type non signé forçant la conversion des deux opérandes au type non signé. (Cela est vrai sur toute mise en œuvre où un signé int ne peut pas tenir toute la gamme possible de valeurs positives d'un unsigned int qui est presque toutes les implémentations.) Changement

3

droit sur les types signés est mise en œuvre définie, en particulier le compilateur est libre de faire un changement arithmétique ou logique à sa guise. C'est quelque chose que vous ne remarquerez pas si la valeur concrète que vous traitez est positive, mais dès qu'elle est négative, vous risquez de tomber dans un piège.

Juste ne le faites pas, ce n'est pas portable.

+0

Techniquement, seul le décalage de droite sur les quantités signées * négatives * est défini par l'implémentation. Le décalage droit des nombres positifs a un comportement défini. – caf

+0

@caf: à droite, je vais rendre ma réponse un peu plus précise. –

0

Si ce code est en Java, vous devez utiliser « >>> » qui est un décalage droit non signé, sinon il signera d'étendre la valeur

+0

C'est C/C++, d'où le tag. –

1

Si vous voulez que cela fonctionne même sur les plates-formes al à la fois signé et entiers non signés, changer

(x & (0xFF << 24)) >> 24 

dans

(x >> 24) & 0xFF 
Questions connexes