ariadne.space/content/blog/the-various-ways-to-check-i...

4.8 KiB

title date
The various ways to check if an integer is even 2021-04-27

You have probably seen this post on Twitter by now:

God I wish there was an easier way to do this pic.twitter.com/8UrBNKdTRW

— Kat Maddox (@ctrlshifti) July 30, 2020

But actually, the way most people test whether a number is even is wrong.  It's not your fault, computers think differently than we do.  And in most cases, the compiler fixes your mistake for you.  But it's been a long day of talking about Alpine governance, so I thought I would have some fun.

However, a quick note: for these examples, I am using ML, specifically the OCaml dialect of it.  Translating these expressions to your language however should not be difficult, and I will provide C-like syntax for the right answer below too.

Using the modulus operator and bitwise math

The usual way people test whether a number is even is the way they teach you in grade school: x mod 2 == 0.  In a C-like language, this would be represented as x % 2 == 0.  However, this is actually quite slow, as the div instruction is quite expensive on most CPUs.

There is a much faster way to check if a number is even or odd, but to understand why it is faster, we should discuss some number theory first.  Whether a number is even or odd ultimately comes down to a single number: 1.

There are two numbers in the entire universe that have the property that they are the same number in any number system we use today: 0 is always zero, and 1 is always one.  This holds true for binary (base 2), octal (base 8), decimal (base 10), and hexadecimal (base 16).

Accordingly, we can use binary logic to test whether a number is even or not by testing whether it ends in 1 when represented as binary.  But many programmers probably don't actually know how to do this -- it doesn't usually come up when you're writing a web app, after all.

The answer is to use logical and: x land 1 == 0 (or in C, x & 1 == 0).  We can prove that both expressions are functionally equivalent, by defining both testing functions and testing for the same output:

# let evenMod x = x mod 2 == 0;; val evenMod : int -> bool =

let evenAnd x = x land 1 == 0;;

val evenAnd : int -> bool =

let evenMatches x = evenMod(x) == evenAnd(x);;

val evenMatches : int -> bool =

evenMatches(0);;

  • : bool = true

evenMatches(1);;

  • : bool = true

evenMatches(2);;

  • : bool = true

evenMatches(3);;

  • : bool = true

As you can see, both are equivalent.  And to be clear, this is the right way to test whether an integer is even.  The other ways below are intended to be a joke.  Also, most compilers will optimize x mod 2 == 0 to x land 1 == 0.

Using functional programming

The nice thing about math is that there's always one way to prove something, especially when there's more than one way.  Modulus operator?  Bitwise logic?  Please.  We're going to solve this problem the way Alonzo Church intended.  But to do that, we need to think about what actually makes a number even.  The answer is simple, of course: an even number is one which is not odd.  But what is an odd number?  Well, one that isn't even of course.

But can we really apply this circular logic to code?  Of course we can!

# let rec isEven x =
  x = 0 || isOdd (x - 1) and isOdd x =   x <> 0 && isEven (x - 1);; val isEven : int -> bool = val isOdd : int -> bool =

isEven(0);;

  • : bool = true

isEven(1);;

  • : bool = false

isEven(2);;

  • : bool = true

isEven(3);;

  • : bool = false

As you can see, we've succeeded in proving that an even number is clearly not odd!

Using pattern matching

In 1962, Bell Labs invented pattern matching, as part of the SNOBOL language.  Pattern matching has become a popular programming language feature, being implemented in not just SNOBOL, but also Erlang, Elixir, Haskell, ML, Rust and many more.  But can we use it to determine if a number is even or odd?  Absolutely.

# let rec isEven x = match x with       | 0 -> true       | 1 -> false       | 2 -> true       | x -> isEven(x - 2);; val isEven : int -> bool =

isEven(0);;

  • : bool = true

isEven(1);;

  • : bool = false

isEven(2);;

  • : bool = true

isEven(3);;

  • : bool = false

isEven(4);;

  • : bool = true

isEven(5);;

  • : bool = false

As you can see, we have demonstrated many ways to test if a number is even or not.  Some are better than others, but others are more amusing.