views:

819

answers:

11

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) == 0) { /* even */ }

I consider the second option to be far more elegant and meaningful, and that's the one I usually use. But it is not only a matter of taste; The actual performance may vary: usually the bitwise operations (such as the logial-and here) are far more efficient than a mod (or div) operation. Of course, you may argue that some compilers will be able to optimize it anyway, and I agree...but some won't.

Another point is that the second one might be a little harder to comprehend for less experienced programmers. On that I'd answer that it will probably only benefit everybody if these programmers take that short time to understand statements of this kind.

What do you think?

The given two snippets are correct only if num is either an unsigned int, or a negative number with a two's complement representation. - As some comments righfuly state.

+10  A: 

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.

Doug T.
Having forgotten doesn't make it safe. With modulo, you may not be assuming anything about negative numbers, but the behavior is undefined anyway! You're safer working with all two's complement machines. Modulo may work for floating point, but produce unexpected results from imprecision, whereas bitwise arithmetic is undefined and causes a type error.
Potatoswatter
+56  A: 

I code for readability first so my choice here is num % 2 == 0. This is far more clear than num & 1 == 0. I'll let the compiler worry about optimizing for me and only adjust if profiling shows this to be a bottleneck. Anything else is premature.

I consider the second option to be far more elegant and meaningful

I strongly disagree with this. A number is even because its congruency modulo two is zero, not because its binary representation ends with a certain bit. Binary representations are an implementation detail. Relying on implementation details is generally a code smell. As others have pointed out, testing the LSB fails on machines that use ones' complement representations.

Another point is that the second one might be a little harder to comprehend for less experienced programmers. On that I'd answer that it will probably only benefit everybody if these programmers take that short time to understand statements of this kind.

I disagree. We should all be coding to make our intent clearer. If we are testing for evenness the code should express that (and a comment should be unnecessary). Again, testing congruency modulo two more clearly expresses the intent of the code than checking the LSB.

And, more importantly, the details should be hidden away in an isEven method. So we should see if(isEven(someNumber)) { // details } and only see num % 2 == 0 once in the definition of isEven.

Jason
Indeed. If you're checking the lowest bit, my first assumption is that you're testing a flag.
Anon.
A number *is* even because its binary representation ends with a certain bit. There is nothing wrong with it and nothing makes this less true.
frunsi
@frusni: wrong. -1 in ones' complement has last bit 0.
Steve Jessop
@frunsi - no it's not. A number is even if %2 gives no remainder. I can think of lots of implementations where a number doesn't end with the LSB - the 6502 doing a 16bit fetch for instance.
Martin Beckett
@frunsi: The definition of even number is a number that is evenly divisible by two. That is, a number that is divisible by two with zero remainder. That is, a number that is congruent to zero modulo two. The definition of evenness has nothing to do with the representation of the number in a specific base (say the fact that it ends in `0`, `2`, `4`, `6` or `8` in decimal, or `0` in binary). It is a consequence of the definition that even numbers have their LSB equal to zero.
Jason
@Downvoter: I'm sure you have a valid reason. I'd be interested in hearing it.
Jason
@frunsi: Who said an even number has the last bit set to 0. What about negative numbers? Will the last bit not depend on weather the integer representation is 1s compliment or 2s compliment.
Martin York
@Jason: I was the downvoter and you probably already down-voted me too. I was wrong, and with the one's complement addition it also makes some sense. Though your previous reasoning was not objective, but my opinion doesn't matter anymore, I was wrong! ;) I omit talking about one's complement in practice...
frunsi
A: 

Both approaches are not obvious especially to someone who is new to programming. You should define an inline function with a descriptive name. The approach you use in it won't matter (micro optimizations most likely won't make your program faster in a noticeable way).

Anyway, I believe 2) is much faster as it doesn't require a division.

Andreas Bonini
You could benchmark it, but (1) doesn't require a division either. Any compiler that does calculate it that way is sufficiently primitive that micro-optimizations are far from your biggest problem.
David Thornley
If you are new to programming, and you do not know what the modulo operator does, then you are probably still in your first programming class.
tgamblin
+3  A: 

Any modern compiler will optimise away the modulo operation, so speed is not a concern.

I'd say using modulo would make things easier to understand, but creating an is_even function that uses the x & 1 method gives you the best of both worlds.

Peter Alexander
+3  A: 

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.

Jon Seigel
+12  A: 

If you're going to say that some compilers won't optimise %2, then you should also note that some compilers use a ones' complement representation for signed integers. In that representation, &1 gives the wrong answer for negative numbers.

So what do you want - code which is slow on some compilers, or code which is wrong on some compilers?

Of course if num is of an unsigned type, or one of the C99 fixed-width integer types (int8_t and so on, which are required to be 2's complement), then this isn't an issue. In that case, I consider %2 to be more elegant and meaningful, and &1 to be a hack that might conceivably be necessary sometimes for performance. I think for example that CPython doesn't do this optimisation, and the same will be true of fully interpreted languages (although then the parsing overhead likely dwarfs the difference between the two machine instructions). I'd be a bit surprised to come across a C or C++ compiler that didn't do it where possible, though, because it's a no-brainer at the point of emitting instructions if not before.

In general, I would say that in C++ you are completely at the mercy of the compiler's ability to optimise. Standard containers and algorithms have n levels of indirection, most of which disappears when the compiler has finished inlining and optimising. A decent C++ compiler can handle arithmetic with constant values before breakfast, and a non-decent C++ compiler will produce rubbish code no matter what you do.

Steve Jessop
Integer representation is typically determined by the host architecture, not the compiler. You could have a compiler that compiles to machines that use one's or two's complement... the compiler writers will decide based on the hardware available (unless they really just don't like speed). Also, you're never going to see one of those machines, because you don't code for computers made before 1970. The only place you'd really see one's complement today is in IP checksums.
tgamblin
It's determined by the implementation, for which I use "compiler" as an informal term. The compiler-writer makes the decision, informed by the target architecture. If we're only talking about what actual common compilers do, that I'm likely to use, then they all perform the optimisation. So "there is no performance difference" is just as true as "integers are 2's complement", and it comes down to taste/style/clarity.
Steve Jessop
"Compiler" is not an informal term for "implementation".
tgamblin
It is. Maybe you don't want it to be, but if you like I'll let you know every time I see someone say "it's up to the compiler" for something that's implementation-dependent, and you can spend the rest of your life 24/7 correcting them all ;-). Anyway, in this case signed representation is implementation-dependent, and as you rightly pointed out, the compiler can do whatever it wants regardless of the target architecture. One option might be much faster than the other.
Steve Jessop
But OK, I suppose I should say "some compilers, some of the time", rather than "some compilers". GCC on one target will do different implementation-defined things than GCC on another target, but for many purposes it is still "the same compiler". But everything which is implementation-dependent, is decided by the compiler. Even if there's only one fast option for a given target, the standard does not require the compiler to take that option.
Steve Jessop
Actually the standard for Java DOES require that integers be two's complement. This is a C++ question, though, and for C/C++ it's up to the implementation.I'm just saying that sane language implementors make this decision based on what the hardware understands. If you really hate saying that integer representation is largely determined by host architecture, then why not just say "implementation" instead of compiler?
tgamblin
I'm honestly not sure, it's probably a kind of laziness. I don't hate saying it, I just don't bother saying it. If I'm talking strictly about the standard, then I say "implementation". Otherwise I say "compiler" because that is what I interact with directly. And I was riffing off what the questioner said, "some compilers will optimise it anyway", not "some implementations" which would be more correct. I guess I could have fixed it by now faster than arguing, I just don't think it's wrong enough to require correction ;-)
Steve Jessop
It wasn't meant to be a huge addendum -- just a small quibble :-). I gave the answer +1 because I agreed :-P.
tgamblin
-1: I'm sorry, but if your architecture does that then you have either fallen through a time hole to the 1970s, or you are going to otherwise know about it, so this becomes a non-question. Frankly the compiler is more likely to segfault, or the logical definition of odd negative numbers turn out to be inconsistent than this to actually bite you.I'd also like to point out that I've never even seen a use for distinguishing between odd and even negative numbers. If a number is less than zero then it is unlikely to be representing anything for which 'oddness' makes sense. rant over kthanksbi
Autopulated
I don't have an architecture. I sometimes want to write portable code to the standard, which other people can run on whatever architecture they like without having to review the code line-by-line for non-portable idioms. So, if my code only works on 2's complement integers, then I will document in its porting notes that it only works on 2's complement integers. If you only ever use one architecture and compiler, then sure, you don't need to worry about the standard. But the question is tagged "C++", not C++ on some particular architecture, so it must beware of the exceptions.
Steve Jessop
+5  A: 

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!

Bill Forster
I suppose that's one reason for `%2`: the precedence of `%` is more intuitive in C.
David Thornley
Yes, I find this is one precedence rule which is not as I'd expect, so I watch out for it always. It bit me hard once, in the early days before decent debuggers, costing goodness knows how many hours. I notice the question was edited quietly very soon after I posted my answer.
Bill Forster
Heck, I'm surprised it wasn't edited to add parens around both expressions. I find it to be good practice to make precedence explicit to the greatest degree possible in order to avoid making someone who is reading the code guess at the meaning.
Adam
I don't want readers to guess either, but I don't like to over parenthesize when the precedence rules are friendly. In those cases I show tight binding using whitespace. For example;if( RANGE_LO<=x No redundant parens cluttering things up and no confusion about meaning.
Bill Forster
I didn't count on the comment format blowing up the indentation of my code (in the previous comment), sorry about that.
Bill Forster
To prove that the OP doesn't have a monopoly on making silly mistakes, I just noticed that the last word of my answer was completely wrong (I said even instead of odd). Now fixed.
Bill Forster
A: 
frunsi
Couple of points: 0%2 is well defined. If you know what division is that your teacher should have explained modules at the same time. It is safe to assume that developers know what it is as we expect a minimum level of maths skills. Negative odd numbers may not have a LSB set to 1.
Martin York
@Martin: 0%2 *is* well defined. That was not my point. Modulo and division will not be explained at the same time in school.
frunsi
To turn your point around, a reader that is new to programming may not know that in two's complement number representations, the LSB is 0 for even numbers. He may not even know the bitwise-and operator! At least the modulo solution has the property of reflecting the mathematical definition of "evenness."
Adam
+8  A: 

You conclusion about performance is based on the popular false premise.

For some reason you insist on translating the language operations into their "obvious" machine counterparts and make the performance conclusions based on that translation. In this particular case you concluded that a bitwise-and & operation of C++ language must be implemented by a bitwise-and machine operation, while a modulo % operation must somehow involve machine division, which is allegedly slower. Such approach is of very limited use, if any.

Firstly, I can't imagine a real-life C++ compiler that would interpret the language operations in such a "literal" way, i.e. by mapping them into the "equivalent" machine operations. Mostly becuase more often than you think the equivalent machine operations simply do not exist.

When it comes to such basic operations with an immediate constant as an operand, any self-respecting compiler will always immediately "understand" that both num & 1 and num % 2 for integral num do exactly the same thing, which will make the compiler generate absolutely identical code for both expressions. Needless to add, the performance is going to be exactly the same.

BTW, this is not called "optimization". Optimization, by definition, is when the compiler decides to deviate from the standard behavior of abstract C++ machine in order to generate the more efficient code (preserving the observable behavior of the program). There's no deviation in this case, meaning that there's no optimization.

Moreover, it is quite possible that on the given machine the most optimal way to implement both is neither bitwise-and nor division, but some other dedicated machine-specific instruction. On top of that, it is quite possible that there's won't be any need for any instruction at all, since even-ness/odd-ness of a specific value might be exposed "for free" through the processor status flags or something like that.

In other words, the efficiency argument is invalid.

Secondly, to return to the original question, the more preferable way to determine the even-ness/odd-ness of a value is certainly the num % 2 approach, since it implements the required check literally ("by definition"), and clearly expresses the fact that the check is purely mathematical. I.e. it makes clear that we care about the property of a number, not about the property of its representation (as would be in case of num & 1 variant).

The num & 1 variant should be reserved for situations when you want access to the bits of value representation of a number. Using this code for even-ness/odd-ness check is a highly questionable practice.

AndreyT
You make a lot of assumptions here, not all of which are correct, but its your attitude that earned you a -1. Its a simple question, you don't have to assassinate the OP.
cyberconte
Most of the statements I made are too generic to be called "incorrect assumptions". So: sorry, everything I said is perfectly correct. If something looks incorrect to you, you have to be more specific. As for the attitude, I'm pretty sure you are imagining something that isn't there.
AndreyT
As for this being a "simple question"... :) No, this is only a "simple question" for those who lack the sufficient depth in understanding the issue.
AndreyT
Agree with @cyberconte. -1 for being an ass. "No C++ compiler in the history of mankind"? Really? Be specific. Also, I challenge you to name a mainstream architecture where there is an instruction *other* than **and** or **division** used to implement this.
tgamblin
@tgamblin: Huh? I don't know whether you know the meaning of the word "specific", but statements "No C++ compiler in the history of mankind" is as specific as it can ever get.
AndreyT
@tgamblin: As for the "architecture" question... The question itself makes no sense. It is not the "architecture" that implements the C++ language operations, it is the compiler that implements them using the available means provided by the hardware architecture. Naming just *one* is difficult because they *all* work as I describbed. No compiler I ever saw would implement `num % 2` through literal division (why would they?). But if you need a specific one (although I don't see the point): X86 + GCC or MSVC.
AndreyT
Also, X86 is one architecture where odd-ness of a value is exposed through the PF CPU flag, meaning that a smart compiler might not generate any instruction at all, if the values was obtained as the result of the last operation.
AndreyT
And one final thing: make sure that from now on when you feel the urge to call someone on SO an "ass" you'll think first (with your head preferrably), take a deep breath and count to 10 before posting. And no, I'm not offering this as an option.
AndreyT
First, its a simple question with a simple answer. Its only complicated if you want it to be. Second, re your last post, you contradict yourself (Most of the statements I made are too generic to be called "incorrect assumptions". / "No C++ compiler in the history of mankind" is as specific as it can ever get), you attempt to overcompensate and belittle (this is only a "simple question" for those who lack the sufficient depth in understanding the issue) and are generally rude, completely obscuring any correct statements you make. I suggest you look in the mirror.
cyberconte
@cyberconte: Firstly, exactly: it is only as complicated as I want it to be. This is my time and my effort. If I think that this issue need a more in-depth explanation, I will provide that explanation. It is my personal bussiness, not yours. Yes, I understand that lazy people usually feel "irritated" by people who are not as lazy, but I'm not going to concern myself with it when I write my answers.
AndreyT
@cyberconte: Secondly, you accusation of contradiction is laughable: there are concerte statements in my reply, there are generic statements in my reply. Both kinds are there. At the same time. I don't know if this is too difficult for you to understand. In fact, the only reason we have to talk about that is becuase you made a sweeping attack in your first comment, without providing any specifics.
AndreyT
@cyberconte: The tone present in my further comments (your "belittle" bit) is the tone set up by *you* in your comments. You are trying to represet it as my fault, but I can only reiterate that you imagined something that simply wan't there originally.
AndreyT
I +1'ed this one. Nice explanation about the difference of *value* operating and *value-representation* operating operations. It also contains the "straigth-forward" argument, and the "you don't know the CPU" argument.
Johannes Schaub - litb
A: 

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.

A. Walton
+5  A: 

It's been mentioned a number of times that any modern compiler would create the same assembly for both options. This reminded me of the LLVM demo page that I saw somewhere the other day, so I figured I'd give it a go. I know this isn't much more than anecdotal, but it does confirm what we'd expect: x%2 and x&1 are implemented identically.

I also tried compiling both of these with gcc-4.2.1 (gcc -S foo.c) and the resultant assembly is identical (and pasted at the bottom of this answer).

Program the first:

int main(int argc, char **argv) {
  return (argc%2==0) ? 0 : 1;
}

Result:

; ModuleID = '/tmp/webcompile/_27244_0.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
target triple = "i386-pc-linux-gnu"

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind readnone {
entry:
    %0 = and i32 %argc, 1  ; <i32> [#uses=1]
    ret i32 %0
}

Program the second:

int main(int argc, char **argv) {
  return ((argc&1)==0) ? 0 : 1;
}

Result:

; ModuleID = '/tmp/webcompile/_27375_0.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
target triple = "i386-pc-linux-gnu"

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind readnone {
entry:
    %0 = and i32 %argc, 1  ; <i32> [#uses=1]
    ret i32 %0
}

GCC output:

.text
.globl _main
_main:
LFB2:
  pushq %rbp
LCFI0:
  movq  %rsp, %rbp
LCFI1:
  movl  %edi, -4(%rbp)
  movq  %rsi, -16(%rbp)
  movl  -4(%rbp), %eax
  andl  $1, %eax
  testl %eax, %eax
  setne %al
  movzbl  %al, %eax
  leave
  ret
LFE2:
  .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
  .set L$set$0,LECIE1-LSCIE1
  .long L$set$0
LSCIE1:
  .long 0x0
  .byte 0x1
  .ascii "zR\0"
  .byte 0x1
  .byte 0x78
  .byte 0x10
  .byte 0x1
  .byte 0x10
  .byte 0xc
  .byte 0x7
  .byte 0x8
  .byte 0x90
  .byte 0x1
  .align 3
LECIE1:
.globl _main.eh
_main.eh:
LSFDE1:
  .set L$set$1,LEFDE1-LASFDE1
  .long L$set$1
ASFDE1:
  .long LASFDE1-EH_frame1
  .quad LFB2-.
  .set L$set$2,LFE2-LFB2
  .quad L$set$2
  .byte 0x0
  .byte 0x4
  .set L$set$3,LCFI0-LFB2
  .long L$set$3
  .byte 0xe
  .byte 0x10
  .byte 0x86
  .byte 0x2
  .byte 0x4
  .set L$set$4,LCFI1-LCFI0
  .long L$set$4
  .byte 0xd
  .byte 0x6
  .align 3
LEFDE1:
  .subsections_via_symbols
mrkj