20

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.

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
rmn
  • 2,386
  • 1
  • 14
  • 21
  • 12
    Use the one that is easiest to read. You should not care about the performance. This is the compilers job. I would bet that after optimization the resulting assembly is exactly the same. – Martin York Dec 22 '09 at 21:28
  • 6
    I just though (&1) may not work with negative numbers. It will depend on if the implementation uses 1-compliment or 2-compliment. – Martin York Dec 22 '09 at 21:42
  • 1
    Martin, you right on both counts. The actual result of / and % is also implementation-defined if at least one argument is negative. Though, in this use-case it's fine. C++0x will adopt the C99-rule that integer division always rounds towards zero. – sellibitze Dec 22 '09 at 21:59
  • I assume that everyone would hate me for even suggesting if(!(n%2)){;} – silentcontributor Dec 22 '09 at 22:22
  • "it will probably only benefit everybody if these programmers take that short time to understand statements of this kind". It's views like that which lead to esoteric unmaintainable code. It's nearly always better to go with the more-readable approach than the performs-slightly-faster. – RJFalconer Dec 22 '09 at 22:28
  • 3
    Is there any compiler written after 1980 that would not generate the same code for the two statements? (ones complement is suggested, and of course those won't, but is there really compilers/chips that don't use twos complement?) – erikkallen Dec 22 '09 at 23:49
  • Duplicate: http://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd – Adam Rosenfield Dec 23 '09 at 01:00
  • num&1 also wont work for non binary numbers. – ctrl-alt-delor May 21 '12 at 11:56
  • Possible duplicate of [How do I check if an integer is even or odd?](https://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd) – S.S. Anne Sep 13 '19 at 01:48

12 Answers12

76

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
  • 236,483
  • 35
  • 423
  • 525
  • 16
    Indeed. If you're checking the lowest bit, my first assumption is that you're testing a flag. – Anon. Dec 22 '09 at 21:34
  • 2
    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 Dec 22 '09 at 21:35
  • 1
    @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 Dec 22 '09 at 21:38
  • 11
    @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 Dec 22 '09 at 21:39
  • @Downvoter: I'm sure you have a valid reason. I'd be interested in hearing it. – jason Dec 22 '09 at 21:41
  • @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 Dec 22 '09 at 21:41
  • @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 Dec 22 '09 at 22:26
  • Yet another reason not to recommended the bit and trick: it is easy to forget the `()` around the bit and expression like the answer did ... – L. F. Sep 13 '19 at 01:09
24

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"? Not necessarily the same compilers in each case, but both kinds are extremely rare.

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
  • 273,490
  • 39
  • 460
  • 699
  • 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. – Todd Gamblin Dec 22 '09 at 21:59
  • 1
    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 Dec 22 '09 at 22:04
  • "Compiler" is not an informal term for "implementation". – Todd Gamblin Dec 22 '09 at 22:06
  • 4
    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 Dec 22 '09 at 22:11
  • 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 Dec 22 '09 at 22:21
  • 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? – Todd Gamblin Dec 22 '09 at 22:25
  • 1
    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 Dec 22 '09 at 23:02
  • It wasn't meant to be a huge addendum -- just a small quibble :-). I gave the answer +1 because I agreed :-P. – Todd Gamblin Dec 23 '09 at 00:04
  • -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 – James Dec 28 '09 at 00:04
  • 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 Dec 30 '09 at 13:59
12

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.
  • 64,223
  • 27
  • 138
  • 202
  • 1
    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 Dec 23 '09 at 00:39
12

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 because more often than one'd 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. Naturally, 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 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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 7
    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 Dec 22 '09 at 22:29
  • 2
    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. – AnT stands with Russia Dec 22 '09 at 22:33
  • 1
    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. – AnT stands with Russia Dec 22 '09 at 22:35
  • 1
    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. – Todd Gamblin Dec 22 '09 at 22:44
  • @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. – AnT stands with Russia Dec 22 '09 at 22:46
  • @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. – AnT stands with Russia Dec 22 '09 at 22:50
  • 3
    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. – AnT stands with Russia Dec 22 '09 at 22:53
  • 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. – AnT stands with Russia Dec 22 '09 at 22:57
  • 3
    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 Dec 22 '09 at 23:05
  • 1
    @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. – AnT stands with Russia Dec 22 '09 at 23:10
  • @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. – AnT stands with Russia Dec 22 '09 at 23:12
  • @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. – AnT stands with Russia Dec 22 '09 at 23:14
  • 3
    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 Dec 22 '09 at 23:20
  • 1
    @AndreyT: regarding the definition of "optimization", whose definition? The C++03 standard says, for example, "To optimize space allocation, a specialization of vector for bool elements is provided". Clearly it uses "optimize" in a more general sense than "deviate from the abstract machine". I would consider that writing code in the compiler to select a more performant instruction in a special case, is one form of "optimization", but if not then what should it be called? Not that any of this detracts from your main argument, since whatever we call it we know that all compilers do it. – Steve Jessop Apr 28 '12 at 00:40
  • @AnT: re: [this comment](https://stackoverflow.com/questions/1949271/would-you-use-num2-or-num1-to-check-if-a-number-is-even#comment1860938_1949511) x86's PF flag is not useful here. Most instructions set it according to the (horizontal) XOR of the low 8 bits of a result: PF=1 means even parity https://en.wikipedia.org/wiki/Parity_flag. i.e. `PF = ~popcnt((uint8_t)x) & 1`. i.e. It's a https://en.wikipedia.org/wiki/Parity_bit. None of this is useful for the *other* common use of the term Parity = odd or even. https://en.wikipedia.org/wiki/Parity_(mathematics) – Peter Cordes Jul 03 '19 at 03:34
  • Perhaps worth mentioning that `x % 2 == 1` is *not* cheap for signed `x`, because `-3 % 2 = -1`, not `+1`, so 2's complement systems have to check the sign bit as well as the low bit. (Unless they can prove that `x` is always non-negative). So yes, `x % 2 == 0` is great and what we should recommend, but there is an efficiency (and correctness) issue to be concerned about with signed modulo if you aren't just comparing against `0`. – Peter Cordes Jul 03 '19 at 03:38
  • In fact your answer has a mistake: you claim that `num & 1` and `num % 2` are equivalent. That's only true when comparing the result to 0. (And on 2's complement or sign/magnitude systems). As Cody's answer shows, until recently even one of the major x86 compilers had missed optimizations for `x % 2 == 0`. But MSVC sucks anyway (especially older MSVC), so I'd still upvote this answer. :P – Peter Cordes Jul 03 '19 at 04:05
9

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
  • 3,041
  • 19
  • 24
  • The original question awakened my morbid curiosity, and I too tested this immediately (before seeing this answer). Functions with body `return ((num & 1) == 0)` and `return ((num % 2) == 0)` where `num` `int` or `unsigned` (four functions in total) all were converted to the same assembly instructions -- I tried with x86_64, mips, and arm using `gcc` versions 4.6.1, 4.4.1, 4.5.0 repectively. – FooF May 21 '12 at 11:32
  • I'd recommend compiling with optimization enabled to simplify the gcc output. And leave out the metadata + debug info after `EH_frame1:`. You can use https://godbolt.org/ to try gcc/clang/ICC/MSVC for various architectures. – Peter Cordes Jul 03 '19 at 03:41
7

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
  • 6,137
  • 3
  • 27
  • 27
  • 3
    I suppose that's one reason for `%2`: the precedence of `%` is more intuitive in C. – David Thornley Dec 22 '09 at 21:51
  • 2
    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 Dec 22 '09 at 22:19
  • 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 Dec 22 '09 at 22:35
  • 1
    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 && x<=RANGE_HI ) z = x*2 + y/3; No redundant parens cluttering things up and no confusion about meaning. – Bill Forster Dec 22 '09 at 23:10
  • I didn't count on the comment format blowing up the indentation of my code (in the previous comment), sorry about that. – Bill Forster Dec 22 '09 at 23:11
  • 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 Dec 23 '09 at 23:10
  • I wish C-derived languages had an binary operator which would report whether the binary intersection of two binary numbers was non-zero. In C, one can simply write `if (x & 1)`, but such usage is disallowed in many other languages which require a `bool` for `if`. If a language were to require `bool` for `if`, but overload `!` to check for non-zeroness of integer type and non-nullness of pointers, then if (!!(x & 1))` would still be IMHO better than `if ((x & 1) != 0)` if the purpose of the code isn't to test whether an expression equals the number zero, but rather to test the zero-ness of it. – supercat Aug 13 '15 at 18:45
3

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
  • 53,344
  • 14
  • 119
  • 168
3

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,251
  • 8
  • 58
  • 92
2

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;   
}
Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • I wonder how those compiler versions do on `(x << y) | (x >> (32-y))` vs `(x << y) | (x >> (31-y) >> 1)`? IMHO, given that the former worked in 99% of C compilers prior to 2009 when using non-pedantic settings, the Standard should have been changed to mandate that on an n-bit machine, `x>>n` must always either `x` or `x>>(n-1)>>1` (selected arbitrarily) or trap in implementation-defined fashion. I'd regard the former code as being in every way superior to the latter were it not for the reinvention of how compilers should behave in cases where the Standard imposes no requirements. – supercat Aug 13 '15 at 18:52
  • 1
    Current MSVC no longer has this missed-optimization bug, fortunately. Godbolt only goes back to VS2015 (CL19.0), where this is fixed. You'd think they'd have bothered to special case `%2` of signed integers when the result is only being checked for non-zero. `x % 2 == 1` is hard, or like `return x % 2` has to return -1, 0, or 1 depending on the sign and low bits for 2's complement. But `x % 2 == 0` is exactly equivalent to `(x&1) == 0` when targeting a 2's complement system like x86. – Peter Cordes Jul 03 '19 at 03:59
  • 1
    And BTW, for a register-arg calling convention like Windows fastcall, the best bet would be `lea eax, [ecx + 1]` to flip the low bit while copying, then `and eax,1` or `and al,1` for code-size if you're returning a narrow bool. But none of gcc/clang/MSVC/ICC spot that. https://gcc.godbolt.org/z/ubvsfx Although clang does choose `test dil,1` / `sete al` for stand-alone functions, but not when inlining into main. – Peter Cordes Jul 03 '19 at 04:03
0

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
  • 44,018
  • 30
  • 122
  • 156
  • 2
    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 Dec 22 '09 at 21:41
  • 1
    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. – Todd Gamblin Dec 22 '09 at 22:02
0

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).

Frunsi
  • 7,099
  • 5
  • 36
  • 42
  • 3
    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 Dec 22 '09 at 22:04
  • @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 Dec 22 '09 at 22:33
  • 2
    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 Dec 22 '09 at 22:41
  • 1
    Interestingly, the binary literal has made its way to C++14: `0b00010111`. – L. F. Sep 13 '19 at 01:15
-2

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
  • 7
  • 2