Que signifie (x ^ 0x1)! = 0?


182

Je suis tombé sur l'extrait de code suivant

if( 0 != ( x ^ 0x1 ) )
     encode( x, m );

Que veut x ^ 0x1dire? Est-ce une technique standard?


97
L'obscurcissement du code est assez standard.
raina77ow

47
Cela peut être résolu avec la même approche qu'une équation «normale»: 0 != (x ^ 1)→ xou les deux côtés par 1 → (0 ^ 1) != (x ^ 1 ^ 1)→ simplifier →1 != x
Score_Under

4
Je ne vois pas comment il if (1 != x)est difficile d'écrire.
Adriano Varoli Piazza

12
Divers commentaires, d'abord par @Spook, indiquent clairement que le typeof xn'est pas donné - par conséquent, nous ne savons pas qu'il s'agit d'un entier dans ce problème étiqueté C ++. Bien sûr, si c'est C ou xest un entier, la réponse est facile, mais ce n'est pas une donnée et la possibilité de surcharge operator ^existe.
chux - Réintégrer Monica

11
Je ne vois pas comment cela obtient autant de votes positifs ...
laike9m

Réponses:


276

L'opération XOR ( x ^ 0x1) inverse le bit 0. Ainsi, l'expression signifie en fait: si le bit 0 de x est 0, ou tout autre bit de x est 1, alors l'expression est vraie.

Inversement, l'expression est fausse si x == 1.

Le test est donc le même que:

if (x != 1)

et est donc (sans doute) inutilement obscurcie.


39
Hé, tu ne connais pas le contexte. Si x est une sorte d'indicateur de bits, il est en fait plus clair pour l'OMI de l'écrire tel qu'il est maintenant que d'utiliser l'opérateur! =.
Spook

40
@Spook: il ne s'agit pas seulement de tester un indicateur à un seul bit, mais de tester toute la largeur de x. Si vous voulez juste tester un seul bit, il existe des expressions idiomatiques plus claires, telles que l'utilisation d'un ET au niveau du bit.
Paul R

115
Inutilement obscurci? Ne savez-vous pas que c'est notre travail de brouiller le code. Si nous écrivions un code simple que tout le monde pourrait comprendre, pourquoi, où irait la sainteté religieuse de nos positions dans le monde? Nous serions soudainement des travailleurs ordinaires comme tout le monde. L'obfuscation est intrinsèquement nécessaire.
Thom

31
En fait, Spook a raison. Le test (x! = 1) n'est pas équivalent. Le code peut être C ++ (et en C ++, ^ peut être un opérateur qui fait n'importe quoi). Donc vous ne connaissez pas le contexte, @Spook a raison.
xryl669

82
@TheThom уєт уσυ ωяιтє ιи ¢ ℓєαя тєχт
bobobobo

78
  • ^est le niveau du bit XOR opération
  • 0x1est 1en notation hexadécimale
  • x ^ 0x1va inverser le dernier bit de x(reportez-vous à la table de vérité XOR dans le lien ci-dessus si ce n'est pas clair pour vous).

Ainsi, la condition (0 != ( x ^ 0x1 ))sera vraie si xest supérieure à 1 ou si le dernier bit de xest 0. Ce qui ne laisse que x == 1 comme valeur à laquelle la condition sera fausse. Donc c'est équivalent à

if (x != 1)

PS Enfer d'un moyen de mettre en œuvre une condition aussi simple, pourrais-je ajouter. Ne fais pas ça. Et si vous devez écrire du code compliqué, laissez un commentaire . Je t'en supplie.


7
Ce n'est pas x==0; 4 ^ 0x1est vrai, mais 4==0est évidemment faux.
Fred Foo

4
"la condition semble être égale à if (x == 0)", n'est-ce pas égal à x != 1?
Andrew-Dufresne

6
L'équivalence présuppose qu'il xs'agit d'un type intégral. Si c'est un floatou double, alors je crois que l'expression donnerait vrai pour 1.0 <= x < 2.0. Et si xest un type défini par l'utilisateur, l'expression peut retourner true si xest un Yugo, un kangourou, l'anniversaire d'un célèbre compositeur ou tout autre nombre qui partage au moins trois chiffres avec le prix actuel du thé en dollars en Chine.
supercat

4
@supercat Il n'y a pas de operator^pour float/ double.
moelleux

1
@supercat: lorsqu'une conversion de virgule flottante en entier est appelée, la conversion est implicite (ne nécessite pas de syntaxe de conversion). Mais aucune conversion n'est déclenchée par des opérateurs binaires, ils échouent simplement pour les types à virgule flottante.
Ben Voigt

49

Cela peut sembler une explication trop simpliste, mais si quelqu'un souhaite la parcourir lentement, c'est ci-dessous:

^est un opérateur XOR au niveau du bit en c, c ++ et c #.

Un XOR au niveau du bit prend deux modèles de bits de longueur égale et exécute l'opération logique OU exclusif sur chaque paire de bits correspondants.

Le OU exclusif est une opération logique qui génère la valeur true chaque fois que les deux entrées diffèrent (l'une est vraie, l'autre est fausse).

La table de vérité d' un xor b :

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

Illustrons donc l' 0 == ( x ^ 0x1 )expression au niveau binaire:

             what? xxxxxxxx (8 bits)
               xor 00000001 (hex 0x1 or 0x01, decimal 1)    
             gives 00000000
---------------------------
the only answer is 00000001

donc:

   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1

34

Il s'agit d'un opérateur OU (XOR) exclusif. Pour comprendre comment cela fonctionne, vous pouvez exécuter ce code simple

    std::cout << "0x0 ^ 0x0 = " << ( 0x0 ^ 0x0 ) << std::endl;
    std::cout << "0x0 ^ 0x1 = " << ( 0x0 ^ 0x1 ) << std::endl;
    std::cout << "0x1 ^ 0x0 = " << ( 0x1 ^ 0x0 ) << std::endl;
    std::cout << "0x1 ^ 0x1 = " << ( 0x1 ^ 0x1 ) << std::endl;

La sortie sera

0x0 ^ 0x0 = 0
0x0 ^ 0x1 = 1
0x1 ^ 0x0 = 1
0x1 ^ 0x1 = 0

Donc cette expression

0 != ( x ^ 0x1 )

sera égal à vrai uniquement lorsque x! = 0x1.

Il ne change pas x lui-même. Il vérifie seulement si x est égal à 0 ou à 1. cette expression r peut être changée en

if ( x != 0x1 )

19

Il vérifie que xest en fait pas 0x1... xorING xavec 0x1entraînera 0 seulement si xest 0x1... c'est un vieux truc utilisé principalement en langage assembleur


Est-ce plus rapide que != 1?
Fiddling Bits

2
dans les temps anciens en faisant des optimisations d'assemblage manuelles (x86) si je me souviens bien, l' xorapproche contenait moins de code machine et était exécutée plus rapidement que l'affectation correspondante à 0... mais cette question contient un xorET une comparaison, donc je pourrais penser que cela !=pourrait être plus rapide. Je ne suis pas si sûr cependant que j'aurais besoin de voir un assemblage généré par le compilateur.
Ferenc Deak

10
@BitFiddlingCodeMonkey: Non. Si XOR était plus rapide que certains tests d'égalité de niveau natif, votre compilateur émettrait des XOR pour tester l'égalité. Par conséquent, les XOR ne sont jamais plus rapides que les tests d'égalité sur l'optimisation des compilateurs. La règle 101 de l'écriture de code rapide est "n'essayez pas d'aider le compilateur. Vous finirez seulement par créer du code illisible qui est plus lent en pratique".
Matt

@fritzone IIRC, les astuces XOR avaient plus à voir avec la sauvegarde des registres, la sauvegarde d'une charge de registre b / c le littéral pouvait être encodé directement dans l'OP dans certains cas, et quelques différences subtiles avec les indicateurs d'état (mais la plupart de mon assemblage était activé 68k et DSP).
mpdonadio

1
@MPD: Normalement, il s'agit d'effacer un registre (au moins sur 386+). xor eax, eax met eax à zéro sur deux octets, mais mov eax, 0 prend trois ou six octets (selon le codage) et prend un peu plus de temps à décoder que la forme xor .
Matt

18

L' ^opérateur est xor au niveau du bit. Et 0x1est le nombre 1, écrit sous forme de constante hexadécimale.

Donc, x ^ 0x1évalue à une nouvelle valeur qui est la même que x, mais avec le bit le moins significatif inversé.

Le code ne fait rien de plus que comparer x à 1, d'une manière très compliquée et obscure.


11

L'opérateur xor (ou exclusif) est le plus couramment utilisé pour inverser un ou plusieurs bits. L'opération consiste à demander si exactement l'un des bits est un, cela conduit à la table de vérité suivante (A et B sont des entrées, Y est une sortie):

A    B    Y
0    0    0
0    1    1
1    0    1
1    1    0

Maintenant, le but de ce code semble être de vérifier si exactement le dernier bit est 1, et les autres sont 0, cela est égal if ( x != 1 ). La raison de cette méthode obscure pourrait être que des techniques antérieures de manipulation de bits ont été utilisées et sont peut-être utilisées à d'autres endroits du programme.


8

^est bitwise xor operatordans c. Dans votre cas, x est xor'ed avec 1. par exemple xa la valeur 10, alors la 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11dcondition devient vraie.


Tapez votre réponse. 10 != 1010
Fiddling Bits

@BitFiddlingCodeMonkey: faute de frappe dans votre commentaire:10 (decimal) == 1010 (binary)
Paul R

6
@PaulR Comment quelqu'un est-il censé savoir ce qui est décimal et ce qui est binaire si vous n'y mettez pas un bou quelque chose?
Fiddling Bits

0b1010 ne serait-il pas la manière pythonique de faire les choses?
TankorSmash

8

Le test au niveau du bit semble être une obfuscation délibérée, mais si les données sous-jacentes sont des données d'entreprise d'un système mainframe IBM, il se peut simplement que le code ait été écrit pour refléter la documentation d'origine. Les formats de données IBM remontent aux années 1960 et codent fréquemment des indicateurs sous forme de bits uniques dans un mot pour économiser le stockage. Au fur et à mesure que les formats étaient modifiés, des octets d'indicateur ont été ajoutés à la fin des enregistrements existants pour maintenir la compatibilité ascendante. La documentation d'un enregistrement SMF, par exemple, peut afficher le code du langage d'assemblage pour tester trois bits individuels dans trois mots différents dans un seul enregistrement afin de décider que les données sont un fichier d'entrée. J'en sais beaucoup moins sur les composants internes TCP / IP, mais vous pouvez également y trouver des bits indicateurs.


7

L'opérateur ^ est le bit-xor (voir &, |). Le résultat pour une paire de bits est,

0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0

Donc l'expression,

( x ^ 0x1 )

inverse / retourne le 0ème bit de x (en laissant les autres bits inchangés).

Considérez si x peut avoir des valeurs autres que 0x0 et 0x1? Lorsque x est un champ à un seul bit, il ne peut avoir que les valeurs 0x0 et 0x1, mais lorsque x est un entier (char / short / long / etc), les bits en plus de bit0 peuvent affecter le résultat de l'expression.

L'expression telle que donnée permet aux bits à côté de bit0 d'affecter le résultat,

if ( 0 != ( x ^ 0x1 ) )

Qui a une véracité équivalente à cette expression (plus simple),

if ( x ^ 0x1 )

Notez que cette expression n'examinerait que bit0,

if( 0x1 & ( x ^ 0x1 ) )

L'expression telle que présentée combine en réalité deux contrôles d'expression,

if( ( x & ~0x1 )  //look at all bits besides bit0
||  ( x ^ 0x1 ) ) //combine with the xor expression for bit0

L'auteur avait-il l'intention de vérifier uniquement bit0, et a-t-il voulu utiliser cette expression,

if( 0x1 & ( x ^ 0x1 ) )

Ou est-ce que l'auteur avait l'intention de combiner les valeurs de bit1-bitN et le xor de bit0?


7

J'ajoute une nouvelle réponse car personne n'a vraiment expliqué comment obtenir la réponse de manière intuitive.

L'inverse de +est -.
L'inverse de ^est ^.

Comment allez-vous résoudre 0 != x - 1pour x? Vous + 1des deux côtés: 0 + 1 != x - 1 + 11 != x.
Comment allez-vous résoudre 0 != x ^ 1pour x? Vous ^ 1des deux côtés: 0 ^ 1 != x ^ 1 ^ 11 != x.


6

Je suppose qu'il y a d'autres bits ou valeurs de champ de bits x, et cela est destiné à tester que seul le bit de poids faible est défini. Dans le contexte, je suppose que c'est la valeur par défaut et que, par conséquent, l'encodage de ceci et de certains connexes m(probablement plus coûteux à encoder) peut être ignoré, car ils doivent tous les deux être la valeur par défaut, initialisée dans un constructeur ou similaire.

D'une manière ou d'une autre, le décodeur doit être capable de déduire que ces valeurs sont manquantes. S'ils sont à la fin d'une structure, cela peut être communiqué via une lengthvaleur toujours présente.


4

Le XOR est utile dans l'énumération d'indicateur C #. Pour supprimer l'indicateur unique de la valeur enum, il est nécessaire d'utiliser l'opérateur xor (référence ici )

Exemple:

[Flags]
enum FlagTest { None 0x0, Test1 0x1, Test2 0x2, Test3 0x4}

FlagTest test = FlagTest.Test2 | FlagTest.Test3;
Console.WriteLine(test); //Out: FlagTest.Test2 | FlagTest.Test3
test = test ^ FlagTest.Test2;
Console.WriteLine(test); //Out: FlagTest.Test3

MAIS la question porte sur C ++, pas C #
theDmi

@theDmi: MAIS AUSSI sur la manipulation de bits et le masque de bits. Flag enum en C # concerne définitivement le masque de bits.
Igrek.

De plus, cela peut également être utile en C ++. Voir: Pile 1 et Pile 2
Igrek.

Cette réponse ne semble pas avoir quoi que ce soit à voir avec la question d'origine, à part une discussion sur l'opérateur XOR au niveau du bit?
Paul R

4

Il y a beaucoup de bonnes réponses mais j'aime y penser d'une manière plus simple.

if ( 0 != ( x ^ 0x1 ) );

Tout d'abord. Une instruction if n'est fausse que si l'argument est zéro. Cela signifie que comparer non égal à zéro est inutile.

if ( a != 0 );
// Same as
if ( a );

Donc, cela nous laisse avec:

if ( x ^ 0x1 );

Un XOR avec un. Ce que fait un XOR est essentiellement de détecter les bits qui sont différents. Donc, si tous les bits sont identiques, il renverra 0. Puisque 0 est faux, la seule fois où il retournera faux est si tous les bits sont identiques. Donc, ce sera faux si les arguments sont les mêmes, vrai s'ils sont différents ... tout comme l' opérateur pas égal à .

if ( x != 0x1 );

En fait, la seule différence entre les deux est que !=retournera 0 ou 1, tandis que ^retournera n'importe quel nombre, mais la véracité du résultat sera toujours la même. Une façon simple d'y penser est.

(b != c) === !!(b ^ c) // for all b and c

La "simplification" finale est la conversion 0x1en décimal qui est 1. Par conséquent, votre déclaration équivaut à:

if ( x != 1 )

1

^ est un opérateur XOR au niveau du bit

Si x = 1

          00000001   (x)       (decimal 1)
          00000001   (0x1)     (decimal 1)
XOR       00000000   (0x0)     (decimal 0)

ici 0 == (x ^ 0x1)

Si x = 0

          00000000   (x)       (decimal 0)
          00000001   (0x1)     (decimal 1)
XOR       00000001   (0x1)     (decimal 0)

ici 0! = (x ^ 0x1)

La table de vérité d'un xor b:

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

Le code signifie simplement


6
Est-il vraiment nécessaire de publier une autre réponse en double?
Mysticial

2
vous pouvez dire une autre réponse mais pas la dupliquer. Désolé si cela vous dérange
akbar ali

1

La technique standard qui pourrait être utilisée, ici, est de répéter un idiome tel qu'il apparaît dans le contexte environnant pour plus de clarté, plutôt que de le masquer en le remplaçant par un idiome qui est arithmétiquement plus simple mais sans signification contextuelle.

Le code environnant peut faire fréquemment référence à (x ^ 1), ou le test peut demander "si le bit 0 était l'inverse, est-ce que ce masque de bits serait vide?".

Étant donné que la condition provoque une modification de quelque chose encode(), il se peut que, dans le contexte, l'état par défaut du bit 0 ait été inversé par d'autres facteurs, et nous n'avons besoin d'encoder des informations supplémentaires que si l'un des bits s'écarte de leur valeur par défaut (normalement tout zéro ).

Si vous sortez l'expression de son contexte et demandez ce qu'elle fait, vous oubliez l'intention sous-jacente. Vous pouvez tout aussi bien regarder la sortie d'assembly du compilateur et voir qu'elle fait simplement une comparaison d'égalité directe avec 1.


0

Comme je vois les réponses jusqu'à présent, il manque une règle simple pour traiter l' XORart. Sans entrer dans les détails de ce que ^et 0xsignifient (et if, et !=etc.), l'expression 0 != (x^1)peut être retravaillée comme suit en utilisant le fait que (a^a)==0:

0 != (x^1) <=> [xor left and right side by 1]
(0^1) != (x^1^1) <=>
1 != x
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.