Is
int even(int n)
{
return (n & 1);
}
a valid way of determining the evenness of a number? For unsigned
integers, yes. For signed positive integers also yes. However,
C99 6.2.6.2-2 states
For signed integer types, the bits of the object representation
shall be divided into three groups: value bits, padding bits, and
the sign bit. [...] If the sign bit is one, the value shall be
modified in one of the following ways
- the corresponding value with the sign bit 0 is negated (sign and
magnitude);
- the sign bit has the value -(2:sup:N)(two's complement);
- the sign bit has the value -(2:sup:N - 1)(one's
complement).
In other words, there are three possible representations of a signed
integer in C, and one of them (one's complement) has a negative
representation that fails the above test. However, I don't think there
are actually any one's complement machines, so you're pretty safe.
However, Murphy's law dictates that eventually your code will run on
some bizarre device that uses one's complement. Thus the best bet is to
use a remainder (%) and let the compiler figure it out for you. Have
a look at what the compiler gets up to for the most straight forward
case:
$ cat test.c
int function(int i)
{
return (i % 2);
}
$ gcc -O2 -S test.c
$ cat test.s
.file "test.c"
.text
.p2align 4,,15
.globl function
.type function, @function
function:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
andl $-2147483647, %eax
js .L5
popl %ebp
ret
.p2align 4,,15
.L5:
decl %eax
orl $-2, %eax
incl %eax
popl %ebp
ret
.size function, .-function
.ident "GCC: (GNU) 4.0.2 20050821 (prerelease) (Debian 4.0.1-6)"
.section .note.GNU-stack,"",@progbits
It's a bit tricky because that remainder needs to account for negative
values. So $-2147483657 == -0x7FFFFFFF, which in two's complement is
binary 1000 0000 0000 0000 0000 0000 0000 0001. So the andl
checks the last bit just like we did before, however having the top bit
set means that if the top bit is set in the other operand (i.e. the sign
bit, indicating it is a negative number), the sign flag will be set and
the js (jump on sign bit) will be triggered (that next bit at
.L5 is a consequence of C99 6.5.5 6 where we have the relation
(a/b)*b + a%b == a).
We can reduce the even function down to
$ cat test.c
int function(int i)
{
return ((unsigned int)i % 2);
}
$ gcc -O2 -S test.c
$ cat test.s
.file "test.c"
.text
.p2align 4,,15
.globl function
.type function, @function
function:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
andl $1, %eax
popl %ebp
ret
.size function, .-function
.ident "GCC: (GNU) 4.0.2 20050821 (prerelease) (Debian 4.0.1-6)"
.section .note.GNU-stack,"",@progbits
Which is the same as the original on this two's complement x86. In this
trivial case the cast to unsigned could have been used with the
original & 1 check and would have been OK, but my feeling is this
the % is a little more clear (and if you need to worry about
negative values your hand is forced).
I think the moral is to be worried whenever you pull out a bitwise
operator and try to pass it off the work to the compiler (with a quick
sanity check).