Swap two integers without using temporary variable

I

indrawati.yahya

OK, before you all stab me with your OT pitchfork, I just want to
mention that this question is indeed related to c.l.c++. My question
is, does the well-known method to swap two integers produce undefined
behaviour in c++? The method is:

a ^= b ^= a ^= b;

It's a clever trick, but I have a feeling something may go wrong if I
use it in C++, although quick tests with a few compilers produce
correct output. So is the code guaranteed to work correctly on all
compilers? Thanks.
 
K

Kai-Uwe Bux

OK, before you all stab me with your OT pitchfork, I just want to
mention that this question is indeed related to c.l.c++. My question
is, does the well-known method to swap two integers produce undefined
behaviour in c++? The method is:

a ^= b ^= a ^= b;

If you write it on a single line, it would appear to be UB since you modify
the variable a twice between sequence points (and even if by some extended
exegesis of the standard it would turn out to be well-defined, it would
still be poor form; but the in the absence of obvious separating sequence
points, the burden to prove well-defined behavior would be on whoever codes
that way. Presumably, it would take a whole paragraph in comments to
justify that line).

However

a ^= b;
b ^= a;
a ^= b;

has well-defined behavior.

It's a clever trick,

No, it isn't. It's just a form of code obfuscation. If you want to swap a
and be, just say so:

swap( a, b );

but I have a feeling something may go wrong if I
use it in C++, although quick tests with a few compilers produce
correct output. So is the code guaranteed to work correctly on all
compilers?

See above.


Best

Kai-Uwe Bux
 
T

terminator

If you write it on a single line, it would appear to be UB since you modify
the variable a twice between sequence points (and even if by some extended
exegesis of the standard it would turn out to be well-defined, it would
still be poor form; but the in the absence of obvious separating sequence
points, the burden to prove well-defined behavior would be on whoever codes
that way. Presumably, it would take a whole paragraph in comments to
justify that line).

However

a ^= b;
b ^= a;
a ^= b;

has well-defined behavior.

I wanted to say so.
No, it isn't. It's just a form of code obfuscation. If you want to swap a
and be, just say so:

swap( a, b );

and what is the implementation of swap?
One may like to implement the int swap as the above three lines.

regards,
FM.
 
K

Kai-Uwe Bux

terminator said:
I wanted to say so.


and what is the implementation of swap?

That is up to the implementor of the library.
One may like to implement the int swap as the above three lines.

So what? Even if std::swap should happen to be implemented in such a way
that it boils down to the above three lines for built-in scalar types, that
still would not imply that

a ^= b; b ^= a; a ^= b;

conveys intend as well and as succinctly as

std::swap( a, b );

There is no point in littering code with hacks. Note: the same applies to

{
int dummy = a;
a = b;
b = dummy;
}

although the case is somewhat weaker. You also would not want to write that
over and over again in your code.


Best

Kai-Uwe Bux
 
M

Marco Manfredini

OK, before you all stab me with your OT pitchfork, I just want to
mention that this question is indeed related to c.l.c++. My question
is, does the well-known method to swap two integers produce undefined
behaviour in c++? The method is:

a ^= b ^= a ^= b;

It's a clever trick, but I have a feeling something may go wrong if I
> use it in C++, although quick tests with a few compilers produce
> correct output. So is the code guaranteed to work correctly on all

A really *stone old* trick, that doesn't work if a and b occupy the same
storage location.

void swap(int &a , int &b)
{
a^=b; b^=a; a^= b;
}

int x=1;
swap(x,x);

will produce 0 in x and can hardly be considered a valid implementation
of swap - which btw. should terminate the other discussion as well.
 
K

Kai-Uwe Bux

Marco said:
A really *stone old* trick, that doesn't work if a and b occupy the same
storage location.

Ah that was it. I remembered vaguely a discussion of several tricks like
this on this group. Unfortunately, I was not able to recall the exact
shortcoming that was pointed out back then (I considered the case a == b
not &a == &b --- and a== b worked).

void swap(int &a , int &b)
{
a^=b; b^=a; a^= b;
}

int x=1;
swap(x,x);

will produce 0 in x and can hardly be considered a valid implementation
of swap - which btw. should terminate the other discussion as well.

Yup.


Thanks a lot

Kai-Uwe Bux
 
R

Ron Natalie

Kai-Uwe Bux said:
So what? Even if std::swap should happen to be implemented in such a way
that it boils down to the above three lines for built-in scalar types, that
still would not imply that

a ^= b; b ^= a; a ^= b;

conveys intend as well and as succinctly as

std::swap( a, b );

There is no point in littering code with hacks. Note: the same applies to

std::swap gets rid of having to worry about it. Presumably your
implementation knows the best way. Once you get past some simple
scalar values, the classes can define their own specialization for
swap.

The people who thing the XOR behavior is a neat idea totally lose the
concept that it's probably the least efficient way to do so. Even
the presumbalby space inefficent:
t = a; a = b; t = b;

might not actually allocate any memory for the temporary value, it
may in fact just be an transient register that may have been allocated
anyway.

LD R1, a
LD R2, b
ST a, R2
ST b, R1

where the XOR debacle
LD R1, a
LD R2, b
XOR R1,R1,R2
XOR R2,R2,R1
XOR R1,R1,R2
ST a, R1
ST b, R2
 
J

James Kanze

OK, before you all stab me with your OT pitchfork, I just want to
mention that this question is indeed related to c.l.c++. My question
is, does the well-known method to swap two integers produce undefined
behaviour in c++? The method is:
a ^= b ^= a ^= b;
It's a clever trick,

It's a stupid trick, which doesn't work reliably in any language.

As written above, it's undefined behavior in C++. But even if
you introduce sequence points, it doesn't work.
but I have a feeling something may go wrong if I
use it in C++, although quick tests with a few compilers produce
correct output.

Only in a few special cases. The basic algorithm doesn't work
reliably, so any implementation using it is bound to fail in
some cases.
 
T

terminator

std::swap gets rid of having to worry about it. Presumably your
implementation knows the best way. Once you get past some simple
scalar values, the classes can define their own specialization for
swap.

The people who thing the XOR behavior is a neat idea totally lose the
concept that it's probably the least efficient way to do so. Even
the presumbalby space inefficent:
t = a; a = b; t = b;

might not actually allocate any memory for the temporary value, it
may in fact just be an transient register that may have been allocated
anyway.

LD R1, a
LD R2, b
ST a, R2
ST b, R1

where the XOR debacle
LD R1, a
LD R2, b
XOR R1,R1,R2
XOR R2,R2,R1
XOR R1,R1,R2
ST a, R1
ST b, R2

even old x86 machines support some 'exg' opcode that swaps two
registers in just one instruction ,so a good compiler can handle
triple assignment much better than you mentioned in case both ints
have register storage.The xor trick has less chance of such
optimization , but it is a joyfull solution to a programming delima(I
like it more than add/subtract solution).

regards,
FM
 
J

James Kanze

even old x86 machines support some 'exg' opcode that swaps two
registers in just one instruction ,so a good compiler can
handle triple assignment much better than you mentioned in
case both ints have register storage.The xor trick has less
chance of such optimization, but it is a joyfull solution to
a programming delima(I like it more than add/subtract
solution).

I seem to recall some 8086 compilers actually recognizing the
classical swap idiom (with the temporary) and generating the
exch instruction to implement it. Modern x86 compilers don't,
however, probably because on more recent x86 processors, xchg
has an implied lock prefix, which acts as a memory fence (which
in turn means that the instruction is considerably slower than
it would be otherwise).

I just ran some quick benchmarks on an Intel based Linux machine
here (using g++ 4.1.0, -O3), using the following "swappers":

struct SwapperClassic
{
void operator()( int& a, int& b )
{
int tmp = a ;
a = b ;
b = tmp ;
}
} ;

struct SwapperXor
{
void operator()( int& a, int& b )
{
a ^= b ;
b ^= a ;
a ^= b ;
}
} ;

struct SwapperAsm
{
void operator()( int& a, int& b )
{
asm ( "movl %[a], %%eax\n xchgl %%eax,% \n movl %%eax,%
[a]"
: [a] "+m" (a), "+m" (b) : : "%eax" ) ;
}
} ;

On this particular machine (not sure of its actual spec's, which
processor or the clock frequency), I got:

ns per machine memory
iter. instr. accesses

SwapperClassic: 1.7 5 5
SwapperXor: 2.5 9 7
SwapperAsm: 84.4 4 4

Tests run on 500 million iterations. In this case, the actual
function was invoked within a virtual member function, and
swapped two member variables. And the last two columns do not
include the standard function prefix or postfix.

A quick glance at the generated assembler showed that none of
the three versions used any local variables.

And as you can see, the cost of the memory fence due to the
implicit lock prefix on the xchgl instruction is very, very
high. The generated code uses one less instruction, and has one
less memory access, but requires roughly 50 times more time to
run.
 
J

Juha Nieminen

James said:
ns per machine memory
iter. instr. accesses

SwapperClassic: 1.7 5 5
SwapperXor: 2.5 9 7

Couldn't the compiler recognize the "I'm trying to outsmart the
compiler when swapping these integers by using the xor trick" idiom and
change it to the classic swapping code?

After all, many compilers detect quite some idioms. For example many
compilers detect that something like "a = (a << 8) | (a >> 24);" is
actually a rotation and compile that as one single opcode.
 
P

Pete Becker

Couldn't the compiler recognize the "I'm trying to outsmart the
compiler when swapping these integers by using the xor trick" idiom and
change it to the classic swapping code?

No, because the xor hack has different semantics from a normal swap.
When you swap a variable with itself its value isn't changed. When you
xor it with itself it gets set to 0.
 
J

James Kanze

Couldn't the compiler recognize the "I'm trying to outsmart the
compiler when swapping these integers by using the xor trick" idiom and
change it to the classic swapping code?

Only if it could prove that the swap didn't involve the same
objects. Since swapping usually takes place through references
and pointers, this would be non-trivial (and not applicable in
most of the really critical cases, in sort, for example). So
you have an optimization that is expensive to check, can rarely
be applied, and would never be applicable to well written code
anyway. Not very interesting.
After all, many compilers detect quite some idioms. For example many
compilers detect that something like "a = (a << 8) | (a >> 24);" is
actually a rotation and compile that as one single opcode.

That's because such an expression is *always* a rotation. And
because there's no easier nor more idiomatic way of expressing
it in C++. Neither is true for the xor swap.
 
T

terminator

even old x86 machines support some 'exg' opcode that swaps two
registers in just one instruction ,so a good compiler can
handle triple assignment much better than you mentioned in
case both ints have register storage.The xor trick has less
chance of such optimization, but it is a joyfull solution to
a programming delima(I like it more than add/subtract
solution).

I seem to recall some 8086 compilers actually recognizing the
classical swap idiom (with the temporary) and generating the
exch instruction to implement it. Modern x86 compilers don't,
however, probably because on more recent x86 processors, xchg
has an implied lock prefix, which acts as a memory fence (which
in turn means that the instruction is considerably slower than
it would be otherwise).

I just ran some quick benchmarks on an Intel based Linux machine
here (using g++ 4.1.0, -O3), using the following "swappers":

struct SwapperClassic
{
void operator()( int& a, int& b )
{
int tmp = a ;
a = b ;
b = tmp ;
}
} ;

struct SwapperXor
{
void operator()( int& a, int& b )
{
a ^= b ;
b ^= a ;
a ^= b ;
}
} ;

struct SwapperAsm
{
void operator()( int& a, int& b )
{
asm ( "movl %[a], %%eax\n xchgl %%eax,% \n movl %%eax,%
[a]"
: [a] "+m" (a), "+m" (b) : : "%eax" ) ;
}
} ;

On this particular machine (not sure of its actual spec's, which
processor or the clock frequency), I got:

ns per machine memory
iter. instr. accesses

SwapperClassic: 1.7 5 5
SwapperXor: 2.5 9 7
SwapperAsm: 84.4 4 4

Tests run on 500 million iterations. In this case, the actual
function was invoked within a virtual member function, and
swapped two member variables. And the last two columns do not
include the standard function prefix or postfix.

A quick glance at the generated assembler showed that none of
the three versions used any local variables.

And as you can see, the cost of the memory fence due to the
implicit lock prefix on the xchgl instruction is very, very
high. The generated code uses one less instruction, and has one
less memory access, but requires roughly 50 times more time to
run.


I wrote that **if both ints have register storage class.Exchanging to
memory **has to lock** the bus (I would be suprised otherwise)for it
is the simplest facility to handle a semaphor.

regards,
FM.
 
T

terminator

Couldn't the compiler recognize the "I'm trying to outsmart the
compiler when swapping these integers by using the xor trick" idiom and
change it to the classic swapping code?

After all, many compilers detect quite some idioms. For example many
compilers detect that something like "a = (a << 8) | (a >> 24);" is
actually a rotation and compile that as one single opcode.

as soome body wrote upthread,'triple_xor_swap(a,a)' would reset 'a' to
zero .Actually my compiler generates 'xor r r' when it needs the 'r'
register to be zero.
OTH 'triple_assign_swap' has well defined behavior it will lead to
straight-forward code that dose not even need to be optimized for some
machines.on a machine that can simultaniously address source and
destination of a move instruction as memory locations ,we might have
something like this:

push reg;
move reg a;
move a b;
move b reg;
pop reg;

regards,
FM.
 
T

terminator

Only if it could prove that the swap didn't involve the same
objects. Since swapping usually takes place through references
and pointers, this would be non-trivial (and not applicable in
most of the really critical cases, in sort, for example). So
you have an optimization that is expensive to check, can rarely
be applied, and would never be applicable to well written code
anyway. Not very interesting.


That's because such an expression is *always* a rotation.

old C books say that it is shift,is the behavoir changed since then?
why not to have a shift (<<) operator along with a rotation(<<<)?

regards,
FM.
 
V

Victor Bazarov

terminator said:
old C books say that it is shift,is the behavoir changed since then?
Nope.

why not to have a shift (<<) operator along with a rotation(<<<)?

Not sure what James meant by the <<*always* a rotation>> bit. My
guess is that in 1970 the machines for which C was developed didn't
have a rotation instruction, and that's why C didn't get a rotation
operator. Whether '<<<' would be the right symbol for it, I am not
sure. Triple-character operators are really only 50% different from
their two-character counterparts and would probably be much less
favorable than, say, '<%' or '>%'.

V
 
K

Kai-Uwe Bux

terminator said:
old C books say that it is shift,is the behavoir changed since then?

I thaught a << 8 is a shift and a >> 24 is a shift. So,

( a << 8 ) | ( a >> 24 )

would be a rotation assuming a bitlength of 32.


[snip]


Best

Kai-Uwe Bux
 
S

Shadowman

James said:
even old x86 machines support some 'exg' opcode that swaps two
registers in just one instruction ,so a good compiler can
handle triple assignment much better than you mentioned in
case both ints have register storage.The xor trick has less
chance of such optimization, but it is a joyfull solution to
a programming delima(I like it more than add/subtract
solution).

I seem to recall some 8086 compilers actually recognizing the
classical swap idiom (with the temporary) and generating the
exch instruction to implement it. Modern x86 compilers don't,
however, probably because on more recent x86 processors, xchg
has an implied lock prefix, which acts as a memory fence (which
in turn means that the instruction is considerably slower than
it would be otherwise).

I just ran some quick benchmarks on an Intel based Linux machine
here (using g++ 4.1.0, -O3), using the following "swappers":

struct SwapperClassic
{
void operator()( int& a, int& b )
{
int tmp = a ;
a = b ;
b = tmp ;
}
} ;

struct SwapperXor
{
void operator()( int& a, int& b )
{
a ^= b ;
b ^= a ;
a ^= b ;
}
} ;

struct SwapperAsm
{
void operator()( int& a, int& b )
{
asm ( "movl %[a], %%eax\n xchgl %%eax,% \n movl %%eax,%
[a]"
: [a] "+m" (a), "+m" (b) : : "%eax" ) ;
}
} ;


What's the advantage of using struct and operator(), instead of simply:

void swapperX(int& a, int& b) ?
 
J

Joe Greer

terminator said:
old C books say that it is shift,is the behavoir changed since then?

I thaught a << 8 is a shift and a >> 24 is a shift. So,

( a << 8 ) | ( a >> 24 )

would be a rotation assuming a bitlength of 32.


[snip]


Best

Kai-Uwe Bux

I don't know about now, but in the olden days, << always 0 filled and >>
was machine dependent. On the machines I used back then, if a were
signed, then >> 1 filled that is, the sign bit got carried so that a >>
1 was always equivalent to a / 2. That would mean that the above would
generate a mess for a signed a rather than a rotation.

joe
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top