What would be the most efficient manner to determine that a number is even using Java, and why?
Would it be using modulo or subtraction, or some other manner that I haven't actually thought of?
One imagines I could determine this doing a simple test class - and I can - but that really wouldn't explain why, would it?
I'm not doing some crazy-pants performance tuning for some lofty goal of processing that many items faster. But I was curious if one method should be preferred over the other as common practice. In the same way we wouldn't use &
in place of &&
, why use %
when we can use &
?
If a number is evenly divisible by 2 with no remainder, then it is even. You can calculate the remainder with the modulo operator % like this num % 2 == 0 . If a number divided by 2 leaves a remainder of 1, then the number is odd. You can check for this using num % 2 == 1 .
To tell whether a number is even or odd, look at the number in the ones place. That single number will tell you whether the entire number is odd or even. An even number ends in 0, 2, 4, 6, or 8. An odd number ends in 1, 3, 5, 7, or 9.
If you check the assembly generated by hotspot 7 of these two methods:
public static boolean isEvenBit(int i) {
return (i & 1) == 0;
}
public static boolean isEvenMod(int i) {
return i % 2 == 0;
}
you will see that although the mod is optimised and basically does a bitwise and
but it has a few extra instructions because the two operations are not strictly equivalent*. Other JVMs might optimise it differently. The assembly is posted below for reference.
I also ran a micro benchmark which confirms our observation: isEventBit is marginally faster (but both run in about 2 nanoseconds so probably won't have much of an inmpact on a typical program as a whole):
Benchmark Mode Samples Score Error Units
c.a.p.SO16969220.isEvenBit avgt 10 1.869 ± 0.069 ns/op
c.a.p.SO16969220.isEvenMod avgt 10 2.554 ± 0.142 ns/op
# {method} 'isEvenBit' '(I)Z' in 'javaapplication4/Test1'
# parm0: rdx = int
# [sp+0x20] (sp of caller)
0x00000000026c2580: sub rsp,0x18
0x00000000026c2587: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry
; - javaapplication4.Test1::isEvenBit@-1 (line 66)
0x00000000026c258c: and edx,0x1
0x00000000026c258f: mov eax,edx
0x00000000026c2591: xor eax,0x1 ;*ireturn
; - javaapplication4.Test1::isEvenBit@11 (line 66)
0x00000000026c2594: add rsp,0x10
0x00000000026c2598: pop rbp
0x00000000026c2599: test DWORD PTR [rip+0xfffffffffdb6da61],eax # 0x0000000000230000
; {poll_return}
0x00000000026c259f: ret
# {method} 'isEvenMod' '(I)Z' in 'javaapplication4/Test1'
# parm0: rdx = int
# [sp+0x20] (sp of caller)
0x00000000026c2780: sub rsp,0x18
0x00000000026c2787: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry
; - javaapplication4.Test1::isEvenMod@-1 (line 63)
0x00000000026c278c: mov r10d,edx
0x00000000026c278f: and r10d,0x1 ;*irem
; - javaapplication4.Test1::isEvenMod@2 (line 63)
0x00000000026c2793: mov r11d,r10d
0x00000000026c2796: neg r11d
0x00000000026c2799: test edx,edx
0x00000000026c279b: cmovl r10d,r11d
0x00000000026c279f: test r10d,r10d
0x00000000026c27a2: setne al
0x00000000026c27a5: movzx eax,al
0x00000000026c27a8: xor eax,0x1 ;*ireturn
; - javaapplication4.Test1::isEvenMod@11 (line 63)
0x00000000026c27ab: add rsp,0x10
0x00000000026c27af: pop rbp
0x00000000026c27b0: test DWORD PTR [rip+0xfffffffffdb6d84a],eax # 0x0000000000230000
; {poll_return}
0x00000000026c27b6: ret
* as pointed out in the comments, %
isn't really modulo; it's the remainder. So (i % 2) != (i & 1)
if i < 0
. The extra instructions in the isEvenMod
code sets the sign of the result to the sign of i
(and then just compares it to zero, so the effort is wasted).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With