xnor variable valores usar una tazas simbolo que pseudocodigo pseint para operacion numéricas negado los lea intercambio intercambie intercambiar hacer funcion enteros elaborar ejemplo ecuacion cómo contenido construye compuerta como cafe auxiliares auxiliar arreglos algoritmo 74ls86 c# swap xor

c# - variable - xor negado



¿Por qué el intercambio de valores con XOR falla al usar esta forma compuesta? (4)

Encontré este código para intercambiar dos números sin usar una tercera variable, usando el operador XOR ^ .

Código:

int i = 25; int j = 36; j ^= i; i ^= j; j ^= i; Console.WriteLine("i:" + i + " j:" + j); //numbers Swapped correctly //Output: i:36 j:25

Ahora cambié el código anterior a este código equivalente.

Mi código:

int i = 25; int j = 36; j ^= i ^= j ^= i; // I have changed to this equivalent (???). Console.WriteLine("i:" + i + " j:" + j); //Not Swapped correctly //Output: i:36 j:0

Ahora, quiero saber, ¿por qué mi código da salida incorrecta?


C # carga j , i , j , i en la pila y almacena cada resultado XOR sin actualizar la pila, por lo que el XOR más a la izquierda usa el valor inicial para j .


Comprueba el IL generado y arroja resultados diferentes;

El intercambio correcto genera una respuesta directa:

IL_0001: ldc.i4.s 25 IL_0003: stloc.0 //create a integer variable 25 at position 0 IL_0004: ldc.i4.s 36 IL_0006: stloc.1 //create a integer variable 36 at position 1 IL_0007: ldloc.1 //push variable at position 1 [36] IL_0008: ldloc.0 //push variable at position 0 [25] IL_0009: xor IL_000a: stloc.1 //store result in location 1 [61] IL_000b: ldloc.0 //push 25 IL_000c: ldloc.1 //push 61 IL_000d: xor IL_000e: stloc.0 //store result in location 0 [36] IL_000f: ldloc.1 //push 61 IL_0010: ldloc.0 //push 36 IL_0011: xor IL_0012: stloc.1 //store result in location 1 [25]

El intercambio incorrecto genera este código:

IL_0001: ldc.i4.s 25 IL_0003: stloc.0 //create a integer variable 25 at position 0 IL_0004: ldc.i4.s 36 IL_0006: stloc.1 //create a integer variable 36 at position 1 IL_0007: ldloc.1 //push 36 on stack (stack is 36) IL_0008: ldloc.0 //push 25 on stack (stack is 36-25) IL_0009: ldloc.1 //push 36 on stack (stack is 36-25-36) IL_000a: ldloc.0 //push 25 on stack (stack is 36-25-36-25) IL_000b: xor //stack is 36-25-61 IL_000c: dup //stack is 36-25-61-61 IL_000d: stloc.1 //store 61 into position 1, stack is 36-25-61 IL_000e: xor //stack is 36-36 IL_000f: dup //stack is 36-36-36 IL_0010: stloc.0 //store 36 into positon 0, stack is 36-36 IL_0011: xor //stack is 0, as the original 36 (instead of the new 61) is xor-ed) IL_0012: stloc.1 //store 0 into position 1

Es evidente que el código generado en el segundo método es incoherente, ya que el valor anterior de j se usa en un cálculo donde se requiere el nuevo valor.


EDITAR: Bien, lo tengo.

El primer punto es que obviamente no deberías usar este código de todos modos. Sin embargo, cuando lo expandes, se convierte en equivalente a:

j = j ^ (i = i ^ (j = j ^ i));

(Si utilizáramos una expresión más complicada como foo.bar++ ^= i , sería importante que el ++ solo se evaluara una vez, pero aquí creo que es más simple).

Ahora, el orden de evaluación de los operandos siempre es de izquierda a derecha, así que para empezar obtenemos:

j = 36 ^ (i = i ^ (j = j ^ i));

Esto (arriba) es el paso más importante. Hemos terminado con 36 como LHS para la operación XOR que se ejecuta por última vez. El LHS no es "el valor de j después de que se haya evaluado el RHS".

La evaluación de la RHS de ^ implica la expresión "un nivel anidado", por lo que se convierte en:

j = 36 ^ (i = 25 ^ (j = j ^ i));

Luego, mirando al nivel más profundo de anidación, podemos sustituir tanto a i como a j :

j = 36 ^ (i = 25 ^ (j = 25 ^ 36));

... que se convierte

j = 36 ^ (i = 25 ^ (j = 61));

La asignación a j en el RHS ocurre primero, pero el resultado se sobrescribe al final de todos modos, por lo que podemos ignorar eso: no hay evaluaciones adicionales de j antes de la asignación final:

j = 36 ^ (i = 25 ^ 61);

Esto ahora es equivalente a:

i = 25 ^ 61; j = 36 ^ (i = 25 ^ 61);

O:

i = 36; j = 36 ^ 36;

Que se convierte en:

i = 36; j = 0;

Creo que eso es todo correcto, y llega a la respuesta correcta ... disculpas a Eric Lippert si algunos de los detalles sobre el orden de evaluación están un poco apagados :(


Reescribiendo:

j ^= i; i ^= j; j ^= i;

Expandiendo ^= :

j = j ^ i; i = j ^ i; j = j ^ i;

Sustituir:

j = j ^ i; j = j ^ (i = j ^ i);

Sustituir esto solo funciona si / porque el lado izquierdo del ^ operador se evalúa primero:

j = (j = j ^ i) ^ (i = i ^ j);

Contraer ^

j = (j ^= i) ^ (i ^= j);

Simétricamente:

i = (i ^= j) ^ (j ^= i);