However, when I look closely to the code, I realized something wasn’t right. I can’t find the original post, so this is what I remember. The function is short so I believe I won’t mess anything up.
function isEven(n: number) {
    return (n ^ 1) === (n + 1);
}
In Javascripts, a number can be an integer, decimal, or NaN, or Inf or
event BigInt. But let’s assume we only use this for integer.
This piece of code is wrong. If we plug a very “small” negative number in, for
example, -9007199249885170 which is greater than Number.MIN_SAFE_INTEGER = -(2^53-1) is a safe integer. We get
const n = -2
isEven(n)
//true
const a = -9007199249885170
isEven(a)
// false
a^1
// 4855823 - WHAT????
We thought we only flipped the last bit (or index 0 bit) of a but somehow everything is messed
up. This is so interesting but I think it deserves an other post when I dive
deep into Javascripts 54 bits two’s complement integer. But for now, let’s stick with
the original problem.
So, normally, we would do n & 1 === 0 to check even number instead.
We can write a simple test to prove our assumption
function realEven(n: number) {
    return (n % 2) === 0;
}
function checkEvenFunction(isEvenFn: (n: number) => boolean) {
    for (let i = Number.MIN_SAFE_INTEGER; i <= Number.MAX_SAFE_INTEGER; i ++) {
        assert.equal(isEvenFn(i), realEven(i), i);
    }
}
function isEvenXor(n: number) {
    return (n ^ 1) === (n + 1);
}
function isEvenAnd(n: number) {
    return (n & 1) === 0;
}
And then call
checkEvenFunction(isEvenXor);
And
checkEvenFunction(isEvenAnd);
😪 I hope you didn’t try this.
Anyway, we have learned how not to check an integer is even in Javascripts. And we’ve done.
Scrolling through comments of the LinkedIn post, there’s a guy who claimed to
be a compiler designer. And he said using % or & don’t matter, because
compilers are smart enough to optimize it. 🤔 an other “wait a minute” moment.
So he’s saying that I have been writing & the whole time without improving
any nanosecond of my Leetcode solutions? IT CAN’T BE!!! But he’s a compiler
designer, he ain’t be wrong… Ok, I have to do fact check myself. Can’t let
this slip through.
#include <stdio.h>
int main() {
    int a;
    scanf("%d", &a);
    if (a % 2) {
        printf("a is odd\n");
    }
    return 0;
}
This is straight forward check, we need to read a from stdin otherwise compiler
would evaluate a % 2 at compile time and eliminate redundant code.
gcc main.c -o main
objdump --disassemble-symbol=main
Let’s checkout the actual assembly code. You can view full assembly code here Online Compiler
// gcc -g -o output.s -masm=intel -fno-verbose-asm -S -fdiagnostics-color=always example.cpp
// ....
        mov     eax, DWORD PTR [rbp-4]
        and     eax, 1
        test    eax, eax
        je      .L2
        mov     edi, OFFSET FLAT:.LC1
        call    puts
.L2:
        mov     eax, 0
        leave
        ret
Without any optimization flag, gcc changes a % 2 to a & 1 .
// clang -O0 ...
// ...
        mov     eax, dword ptr [rbp - 8]
        mov     ecx, 2
        cdq
        idiv    ecx
        cmp     edx, 0
        je      .LBB0_2
        lea     rdi, [rip + .L.str.1]
        mov     al, 0
        call    printf@PLT
No optimization flag -O0
For clang, it still use idiv and get modulo from edx registry. Therefore,
without optimization flag, clang keep our code as it is.
// clang -O3
// ...
        test    byte ptr [rsp + 4], 1
        je      .LBB0_2
        lea     rdi, [rip + .Lstr]
        call    puts@PLT
Aggressive optimization flag -O3
Trying optimization flag from -O1 to -O3 give the same result. We see clang
use test instruction to compare bits of a with number 1. It is equivalent
of a & 1. So we can conclude that clang also optimize modulo check if we ask
for optimization.
Note that this only work with if (n % 2) check, not assignment expression
like int x = n % 2. Because the result would be assigned to x and negative
numbers have negative modulo. Therefore, compiler can’t replace it with & 1 always
produce either 0 or 1.
And according to this article from
Leetcode,
they compile C/C++ with -O2 flag. So my n & 1 manual optimization is
wasted 🤣
Now I wonder how V8 optimize this kind of logic with integer number 🤔 . I don’t have
d8 built on my computer now, so this has to be delayed a bit.