Well, there are at least two low-level ways of determining whether a given number is even or not:
1. if (num%2 == 0) { /* even */ }
2. if ((num&1) ==
At this point, I may be just adding to the noise, but as far as readability goes, the modulo option makes more sense. If your code is not readable, it's practically useless.
Also, unless this is code to be run on a system that's really strapped for resources (I'm thinking microcontroller), don't try to optimize for the compiler's optimizer.
I define and use an "IsEven" function so I don't have to think about it, then I chose one method or the other and forget how I check if something is even.
Only nitpick/caveat is I'd just say that with the bitwise operation, you're assuming something about the representation of the numbers in binary, with modulo you are not. You are interpreting the number as a decimal value. This is pretty much guaranteed to work with integers. However consider that the modulo would work for a double, however the bitwise operation would not.
I don't think the modulo makes things more readable. Both make sense, and both versions are correct. And computers store numbers in binary, so you can just use the binary version.
The compiler may replace the modulo version with an efficient version. But that sounds like an excuse for prefering the modulo.
And readability in this very special case is the same for both versions. A reader that is new to programming may not even know that you can use modulo 2 to determine the even-ness of a number. The reader has to deduce it. He may not even know the modulo operator!
When deducing the meaning behind the statements, it could even be easier to read the binary version:
if( ( num & 1 ) == 0 ) { /* even */ }
if( ( 00010111b & 1 ) == 0 ) { /* even */ }
if( ( 00010110b & 1 ) == 0 ) { /* odd */ }
(I used the "b" suffix for clarification only, its not C/C++)
With the modulo version, you have to double-check how the operation is defined in its details (e.g. check documentation to make sure that 0 % 2
is what you expect).
The binary AND
is simpler and there are no ambiguities!
Only the operator precedence may be a pitfall with binary operators. But it should not be a reason to avoid them (some day even new programmers will need them anyway).
They're both pretty intuitive.
I'd give a slight edge to num % 2 == 0
, but I really don't have a preference. Certainly as far as performance goes, it's probably a micro-optimization, so I wouldn't worry about it.
I spent years insisting that any reasonable compiler worth the space it consumes on disk would optimize num % 2 == 0
to num & 1 == 0
. Then, analyzing disassembly for a different reason, I had a chance to actually verify my assumption.
It turns out, I was wrong. Microsoft Visual Studio, all the way up through version 2013, generates the following object code for num % 2 == 0
:
and ecx, -2147483647 ; the parameter was passed in ECX
jns SHORT $IsEven
dec ecx
or ecx, -2
inc ecx
$IsEven:
neg ecx
sbb ecx, ecx
lea eax, DWORD PTR [ecx+1]
Yes, indeed. This is in Release mode, with all optimizations enabled. You get virtually equivalent results whether building for x86 or x64. You probably won't believe me; I barely believed it myself.
It does essentially what you would expect for num & 1 == 0
:
not eax ; the parameter was passed in EAX
and eax, 1
By way of comparison, GCC (as far back as v4.4) and Clang (as far back as v3.2) do what one would expect, generating identical object code for both variants. However, according to Matt Godbolt's interactive compiler, ICC 13.0.1 also defies my expectations.
Sure, these compilers are not wrong. It's not a bug. There are plenty of technical reasons (as adequately pointed out in the other answers) why these two snippets of code are not identical. And there's certainly a "premature optimization is evil" argument to be made here. Granted, there's a reason it took me years to notice this, and even then I only stumbled onto it by mistake.
But, like Doug T. said, it is probably best to define an IsEven
function in your library somewhere that gets all of these little details correct so that you never have to think about them again—and keep your code readable. If you regularly target MSVC, perhaps you'll define this function as I've done:
bool IsEven(int value)
{
const bool result = (num & 1) == 0;
assert(result == ((num % 2) == 0));
return result;
}
It all depends on context. I actually prefer the &1 approach myself if it's a low level, system context. In many of these kinds of contexts, "is even" basically means has low bit zero to me, rather than is divisible by two.
HOWEVER: Your one liner has a bug.
You must go
if( (x&1) == 0 )
not
if( x&1 == 0 )
The latter ANDs x with 1==0, ie it ANDs x with 0, yielding 0, which always evaluates as false of course.
So if you did it exactly as you suggest, all numbers are odd!