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.