¿Qué significa (x ^ 0x1)! = 0?

183

Me encontré con el siguiente fragmento de código

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

Que x ^ 0x1significa ¿Es esta una técnica estándar?

KodeWarrior
fuente
97
La ofuscación del código es bastante estándar.
raina77ow
47
Esto se puede resolver con el mismo enfoque que una ecuación "normal": 0 != (x ^ 1)→ xo ambos lados por 1 → (0 ^ 1) != (x ^ 1 ^ 1)→ simplificar →1 != x
Puntaje_Debajo del
44
No veo cómo if (1 != x)es difícil de escribir.
Adriano Varoli Piazza
12
Varios comentarios, por primera vez por @Spook, señalan claramente que la typedel xno se da - por lo tanto no sabemos que esto es un número entero en C ++ este problema etiquetado. Claro, si esto es C o xes un número entero, la respuesta es fácil, pero eso no es un hecho y operator ^existe la posibilidad de sobrecarga .
chux - Restablece a Mónica el
11
No veo cómo esto obtiene tantos votos positivos ...
laike9m

Respuestas:

277

La operación XOR ( x ^ 0x1) invierte el bit 0. Por lo tanto, la expresión significa efectivamente: si el bit 0 de x es 0, o cualquier otro bit de x es 1, entonces la expresión es verdadera.

Por el contrario, la expresión es falsa si x == 1.

Entonces la prueba es la misma que:

if (x != 1)

y por lo tanto (posiblemente) se ofusca innecesariamente.

Paul R
fuente
39
Oye, no conoces el contexto. Si x es algún tipo de indicador de bit, es IMO en realidad más claro escribirlo como está ahora que usar el operador! =.
Spook
40
@Spook: sin embargo, no se trata solo de probar un indicador de un solo bit, sino de todo el ancho de x. Si solo desea probar un solo bit, existen modismos más claros, como el uso de un AND bit a bit.
Paul R
115
Innecesariamente ofuscado? ¿No sabes que es nuestro trabajo ofuscar el código? Si escribiéramos un código simple que cualquiera pudiera entender, ¿por qué, a dónde iría la santidad religiosa de nuestras posiciones en el mundo? De repente, seríamos trabajadores comunes como todos los demás. La ofuscación es inherentemente necesaria.
Thom
31
En realidad, Spook tiene razón. La prueba (x! = 1) no es equivalente. El código puede ser C ++ (y en C ++, ^ puede ser un operador que haga cualquier cosa). Así que no sabes el contexto, @Spook tiene razón.
xryl669 19/12/2013
82
@TheThom уєт уσυ ωяιтє ιи ¢ ℓєαя тєχт
bobobobo
78
  • ^es la operación XOR bit a bit
  • 0x1está 1en notación hexadecimal
  • x ^ 0x1invertirá el último bit de x(consulte la tabla de verdad XOR en el enlace de arriba si no está claro para usted).

Entonces, la condición (0 != ( x ^ 0x1 ))será verdadera si xes mayor que 1 o si el último bit de xes 0. Lo que solo deja x == 1 como un valor en el que la condición será falsa. Entonces es equivalente a

if (x != 1)

PD: Una manera increíble de implementar una condición tan simple, podría agregar. No hagas eso. Y si debe escribir un código complicado, deje un comentario . Te lo ruego.

Jirafa violeta
fuente
77
No lo es x==0; 4 ^ 0x1es cierto, pero 4==0obviamente es falso.
Fred Foo
44
"la condición parece ser igual a if (x == 0)", ¿no es igual a x != 1?
Andrew-Dufresne
66
La equivalencia presupone que xes un tipo integral. Si es un floato double, entonces creo que la expresión sería cierta para 1.0 <= x < 2.0. Y si xes un tipo definido por el usuario, la expresión podría ser verdadera si se xtrata de un Yugo, un canguro, el cumpleaños del famoso compositor o cualquier número que comparta al menos tres dígitos con el precio actual del té denominado en dólares en China.
supercat
44
@supercat No hay operator^para float/ double.
esponjoso
1
@supercat: cuando se requiere una conversión de punto flotante a entero, la conversión es implícita (no requiere sintaxis de conversión). Pero los operadores bit a bit no activan ninguna conversión, simplemente fallan para los tipos de punto flotante.
Ben Voigt
49

Esto puede parecer una explicación demasiado simplificada, pero si a alguien le gustaría hacerlo lentamente, está debajo:

^es un operador XOR bit a bit en c, c ++ y c #.

Un XOR bit a bit toma patrones de dos bits de igual longitud y realiza la operación OR exclusiva lógica en cada par de bits correspondientes.

OR exclusivo es una operación lógica que genera verdadero siempre que ambas entradas difieran (una es verdadera, la otra es falsa).

La tabla de verdad de a xor b :

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

Así que vamos a ilustrar la 0 == ( x ^ 0x1 )expresión a nivel binario:

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

entonces:

   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1
jwaliszko
fuente
34

Es operador exclusivo OR (XOR). Para entender cómo funciona, puede ejecutar este código 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 salida será

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

Entonces esta expresión

0 != ( x ^ 0x1 )

será igual de verdadero solo cuando x! = 0x1.

No cambia x sí mismo. Solo comprueba si x es igual a 0 o 1. esta rxpression podría cambiarse a

if ( x != 0x1 )
Vlad de Moscú
fuente
19

Se comprueba que xen realidad no es 0x1... xorING xcon 0x1resultará en 0 sólo si xes 0x1... esto es un viejo truco utilizado sobre todo en lenguaje ensamblador

Ferenc Deak
fuente
¿Es esto más rápido que != 1?
Fiddling Bits
2
en la antigüedad mientras hacía optimizaciones de ensamblaje manual (x86) si recuerdo correctamente, el xorenfoque contenía menos código de máquina y se ejecutó más rápido que la asignación correspondiente a 0... sin embargo, esta pregunta contiene un xorAND y una comparación, por lo que podría pensar que el!= podría ser Más rápido. Sin embargo, no estoy tan seguro, necesitaría ver un ensamblado generado por el compilador.
Ferenc Deak
10
@BitFiddlingCodeMonkey: No. Si XOR fuera más rápido que alguna prueba de igualdad de nivel nativa, su compilador emitiría XOR para probar la igualdad. Por lo tanto, los XOR nunca son más rápidos que las pruebas de igualdad en la optimización de compiladores. La regla 101 de escribir código rápido es "no intentes ayudar al compilador. Solo terminarás creando código ilegible que es más lento en la práctica".
Matt
@fritzone IIRC, los trucos XOR tenían más que ver con guardar registros, guardar una carga de registro b / c, el literal podría codificarse directamente en el OP en algunos casos, y algunas diferencias sutiles con indicadores de estado (pero la mayor parte de mi ensamblaje estaba encendido 68k y DSP).
mpdonadio
1
@MPD: Normalmente tiene que ver con borrar un registro (al menos en 386+). xor eax, eax establece eax a cero en dos bytes, pero mov eax, 0 toma tres o seis bytes (dependiendo de la codificación), y toma un poco más de tiempo decodificar que la forma xor .
Matt
18

El ^operador es bit a bit xor. Y 0x1es el número 1, escrito como una constante hexadecimal.

Por lo tanto, se x ^ 0x1evalúa a un nuevo valor que es el mismo que x, pero con el bit menos significativo invertido.

El código no hace nada más que comparar x con 1, de una manera muy complicada y oscura.

David Heffernan
fuente
11

El operador xor (exclusivo o) se usa más comúnmente para invertir uno o más bits. La operación es preguntar si exactamente uno de los bits es uno, esto lleva a la siguiente tabla de verdad (A y B son entradas, Y es salida):

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

Ahora, el propósito de este código parece ser verificar si excatly el último bit es 1, y los otros son 0, esto es igual if ( x != 1 ). La razón de este método oscuro podría ser que se han utilizado técnicas de manipulación de bits anteriores y tal vez se utilizan en otros lugares del programa.

Pablo
fuente
8

^es bit xor operatora bit adentro c. En su caso, x es xor'ed con 1. por ejemplo xtiene el valor 10, entonces la 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11dcondición se vuelve verdadera.

Chinna
fuente
Error tipográfico en tu respuesta. 10 != 1010
Fiddling Bits
@BitFiddlingCodeMonkey: error tipográfico en su comentario:10 (decimal) == 1010 (binary)
Paul R
66
@PaulR ¿Cómo se supone que alguien sepa qué es decimal y qué es binario si no pone un bo algo allí?
Fiddling Bits
¿No sería 0b1010 la forma pitónica de hacer las cosas?
TankorSmash
8

La prueba bit a bit parece ser una ofuscación deliberada, pero si los datos subyacentes son datos corporativos de un sistema mainframe de IBM, puede ser simplemente que el código se escribió para reflejar la documentación original. Los formatos de datos de IBM se remontan a la década de 1960 y con frecuencia codifican banderas como bits individuales dentro de una palabra para ahorrar almacenamiento. A medida que se modificaron los formatos, se agregaron bytes de marca al final de los registros existentes para mantener la compatibilidad con versiones anteriores. La documentación para un registro SMF, por ejemplo, podría mostrar el código del lenguaje ensamblador para probar tres bits individuales dentro de tres palabras diferentes en un solo registro para decidir que los datos eran un archivo de entrada. Sé mucho menos sobre los componentes internos de TCP / IP, pero también puede encontrar indicadores de bits allí.

indefinido
fuente
7

El operador ^ es bitwise-xor (ver &, |). El resultado para un par de bits es,

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

Entonces la expresión,

( x ^ 0x1 )

invierte / voltea el bit 0 de x (dejando otros bits sin cambios).

Considere si x puede tener valores además de 0x0 y 0x1? Cuando x es un campo de un solo bit, solo puede tener valores 0x0 y 0x1, pero cuando x es un int (char / short / long / etc), los bits además de bit0 pueden afectar el resultado de la expresión.

La expresión dada permite que los bits al lado de bit0 afecten el resultado,

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

Que tiene una veracidad equivalente a esta expresión (más simple),

if ( x ^ 0x1 )

Tenga en cuenta que esta expresión examinaría solo bit0,

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

Entonces, la expresión presentada realmente combina dos verificaciones de expresión,

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

¿El autor tenía la intención de verificar solo bit0, y tenía la intención de usar esta expresión,

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

¿O el autor tenía la intención de combinar los valores para bit1-bitN y el xor de bit0?

ChuckCottrill
fuente
7

Estoy agregando una nueva respuesta porque nadie realmente explicó cómo obtener la respuesta intuitivamente.

El inverso de +es -.
El inverso de ^es ^.

¿Cómo resolver 0 != x - 1para x? Usted + 1a ambos lados: 0 + 1 != x - 1 + 11 != x.
¿Cómo resolver 0 != x ^ 1para x? Usted ^ 1a ambos lados: 0 ^ 1 != x ^ 1 ^ 11 != x.

usuario541686
fuente
6

Supongo que hay otros bits o valores de campo de bits x, y esto está destinado a probar que solo se establece el bit de orden inferior. En el contexto, supongo que este es el valor predeterminado y, por lo tanto, la codificación de esto y algunos relacionadosm , por lo tanto, se puede omitir la (probablemente más caros de codificar), porque ambos deben ser el valor predeterminado, inicializados en un constructor o similar.

De alguna manera, el decodificador debe poder inferir que faltan estos valores. Si están al final de alguna estructura, puede comunicarse a través de un lengthvalor que siempre está presente.

Ed Staub
fuente
4

El XOR es útil en la enumeración de bandera de C #. Para eliminar un solo indicador del valor de enumeración, es necesario utilizar el operador xor (consulte aquí )

Ejemplo:

[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
Igrek
fuente
PERO la pregunta es sobre C ++, no C #
theDmi
@theDmi: PERO TAMBIÉN sobre manipulación de bits y máscara de bits. Marcar enum en C # definitivamente se trata de bitmask.
Igrek.
Además, también puede ser útil en C ++. Ver: Pila 1 y Pila 2
Igrek.
¿Esta respuesta no parece tener nada que ver con la pregunta original, aparte de alguna discusión sobre el operador XOR bit a bit?
Paul R
4

Hay muchas buenas respuestas, pero me gusta pensar de una manera más simple.

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

Ante todo. Una declaración if solo es falsa si el argumento es cero. Esto significa que comparar no igual a cero no tiene sentido.

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

Entonces eso nos deja con:

if ( x ^ 0x1 );

Un XOR con uno. Lo que hace un XOR es esencialmente detectar bits que son diferentes. Entonces, si todos los bits son iguales, devolverá 0. Dado que 0 es falso, la única vez que devolverá falso es si todos los bits son iguales. Por lo tanto, será falso si los argumentos son los mismos, verdadero si son diferentes ... al igual que el operador no igual a .

if ( x != 0x1 );

De hecho, la única diferencia entre los dos es que !=devolverá 0 o 1, mientras ^que devolverá cualquier número, pero la veracidad del resultado siempre será la misma. Una manera fácil de pensarlo es.

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

La "simplificación" final es convertir 0x1a decimal, que es 1. Por lo tanto, su declaración es equivalente a:

if ( x != 1 )
Kevin Cox
fuente
1

^ es un operador XOR bit a bit

Si x = 1

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

aquí 0 == (x ^ 0x1)

Si x = 0

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

aquí 0! = (x ^ 0x1)

La tabla de verdad de a xor b:

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

El código simplemente significa

akbar ali
fuente
66
¿Es realmente necesario publicar otra respuesta duplicada?
Mysticial
2
puedes decir otra respuesta pero no duplicarla. lo siento si usted importa
Akbar Ali
1

La técnica estándar que podría estar siendo utilizada, aquí, es repetir un modismo tal como aparece en el contexto circundante para mayor claridad, en lugar de ofuscarlo reemplazándolo por un modismo que es aritméticamente más simple pero contextualmente sin sentido.

El código circundante puede hacer referencia frecuente a (x ^ 1) , o la prueba puede estar preguntando "si el bit 0 fuera al revés, ¿esta máscara de bits estaría vacía?".

Dado que la condición hace que algo sea encode() edite , puede ser que, en contexto, el estado predeterminado del bit 0 haya sido invertido por otros factores, y solo necesitamos codificar información adicional si alguno de los bits se desvía de su valor predeterminado (normalmente todo cero )

Si toma la expresión fuera de contexto y pregunta qué hace, pasa por alto la intención subyacente. También podría mirar la salida del ensamblaje del compilador y ver que simplemente hace una comparación de igualdad directa con 1.

sh1
fuente
0

Como veo, las respuestas hasta ahora pierden una regla simple para manejar XORs. Sin entrar en detalles sobre qué ^y qué 0xsignifican (y if, y !=etc.), la expresión 0 != (x^1)se puede reelaborar de la siguiente manera utilizando el hecho de que (a^a)==0:

0 != (x^1) <=> [xor left and right side by 1]
(0^1) != (x^1^1) <=>
1 != x
Serge Rogatch
fuente