Assembler XOR 'trick'
Assembler arithmetic 'trick'
Attempting to swap directly using XOR in C and C++
Attempting to swap using XOR by reference in C and C++
Attempting to swap using use arithmetic in C and C++
Assembler XOR 'trick'
Historically swapping values without a temporary was useful in assembler to save on registers/memory, and someone came up with a
way of using XOR do this. Essentially, all you need to do is:
a = a xor b
b = a xor b
a = a xor b
As an example:
let a = 0x55 (0b01010101)
let b = 0xAA (ob10101010)
a = a xor b a == 0x55 0b01010101
b == 0xaa 0b10101010
xor(a) = 0xff 0b11111111
b = a xor b a == 0x55 0b11111111
b == 0xaa 0b10101010
xor(b) = 0xff 0b01010101
a = a xor b a == 0x55 0b11111111
b == 0xaa 0b01010101
xor(a) = 0xff 0b10101010
Since assembler tends to accees memory on a byte by byte or word by
word basis without any regard as to what those values represent or how
they should be interpreted, this will always work, no matter what
values a and b have, nor how 'wide' they are (bytes, words or
doublewords).x ^= yx = x ^ ya ^= b
b ^= a
a ^= b
And if you're allowed to chain expressions, this can even be made even 'cuter' by doing:a ^= b ^= a ^= b
a = a + b
b = a - b
a = a - b
Similar example (presuming twos complement):
let a = 0x55 (0b01010101)
let b = 0xAA (ob10101010)
a = a + b a == 0x55 0b01010101
b == 0xaa 0b10101010
a+b(a) = 0xff 0b11111111 (with a carry)
b = a - b a == 0xff 0b11111111
-b == 0x56 0b01010110
a+-b(b) = 0x55 0b01010101 (with carry)
a = a - b a == 0xff 0b11111111
-b == 0xab 0b10101011
a+-b(a) = 0x55 0b10101010 (with carry)
Again - it doesn't matter what these numbers represent, or how they should be interpreted since they are just 'raw bits' from the point of view of assembler.
The XOR trick can only be used in C and C++ if the the variable is of type int. (This includes long, char and any type that is a typedef of these.)
This means that if any of the variables are of type float, a pointer to anything or anything that isn't an int, attempting to use an XOR in an expression results in undefined behaviour (i.e anything can happen, including what you think to be the right answer, however it isn't guaranteed.) This is because of the following section in The Standard:
ISOIEC9899-1999
6.5.11 Bitwise exclusive OR operator
Syntax
1: exclusive-OR-expression:
AND-expression
exclusive-OR-expression ^ AND-expression
Constraints
2: Each of the operands shall have integer type.
Semantics
3: The usual arithmetic conversions are performed on the operands.
4: The result of the ^ operator is the bitwise exclusive OR of the operands (that is, each bit
in the result is set if and only if exactly one of the corresponding bits in the converted
operands is set).
I've highlighted the relevant points in red. Note that floats are converted into integers, then XOR'd; this clearly will not do what you want. Pointers likewise are converted into ints, and this is implementation defined (i.e. what happens on your compiler is fixed, but may be different from the result of a different compiler):
ISOIEC9899-1999
6.3.2.3 Pointers
1 A pointer to void may be converted to or from a pointer to any incomplete or object
type. A pointer to any incomplete or object type may be converted to a pointer to void
and back again; the result shall compare equal to the original pointer.
2 For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to
the q-qualified version of the type; the values stored in the original and converted pointers
shall compare equal.
3 An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.55) If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.
4 Conversion of a null pointer to another pointer type yields a null pointer of that type.
Any two null pointers shall compare equal.
5 An integer may be converted to any pointer type. Except as previously specified, the
result is implementation-defined, might not be correctly aligned, might not point to an
entity of the referenced type, and might be a trap representation.
Attempting to swap using XOR by reference in C and C++
Another method using XOR that is seen in the context of C and C++ is when you pass by reference to a function called swap (this is written using pointers in C, but the same principle holds for C++ and the reference operator). Note that the caveats mentioned above hold - namely that the parameters to this function must be of integer type:
void swap(int* a, int *b){
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
While this will work for two different variables, it breaks when you pass in the same variable twice:
int x = 10;
swap(&x, &x);
The first statement translates to:x = 10 ^ 10;
a = a + b
b = a - b
a = a - b
When the sum of a and b exceeds MAX_INT (or whatever the maximum for the datatype is), the behaviour is undefined:
double a = 1e-100;
double b = 1e+100;
The first line (a+b) results in the number:
1000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000.00000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000001
Which ANSI/IEEE 754-1985 (IEEE Standard for Binary Floating-Point Arithmetic) cannot store in 32 bits, so it truncates it back to 1e100In article"Malcolm" writes: > "Nils O. Selåsdal" > > What if a+b overflows ? > > And is there any point to do "smart" tricks like this ? If one > > needs to swap variables, do it the right way. If there is > > anything to gain, trust your compiler to optimize it. > > > You're basically right, but not about trusting the compiler. Let's say that > the XOR hack saves a cycle over use of a temporary, due to the architecture > of the instruction set. Seeing that assignment to a temporary is only for > the purposes of swapping is the sort of thing compilers tend not to be good > at, Compilers tend to be very good at just that. Using the tmp variable will much more likely get the optimal sequence: load a to r1 load b to r2 store r2 to a store r1 to b on most machines. Using the xor trick (or whatever) may very well lead to: load a to r1 load b to r2 set r1 to r1 ^ r2 set r2 to r2 ^ r1 set r1 to r1 ^ r2 store r1 to a store r2 to b on the same machines.