Building a cursed binary clock
I recently downloaded a watch face for my Garmin which is in binary. This is great fun! But also it got me thinking... The way this watch face represents time is a collection of bits per digit:
e.g.
◻︎◼︎ = 1
◻︎◼︎◻︎◼︎ = 5
◻︎◼︎◻︎ = 2
◻︎◼︎◻︎◻︎ = 4
Becomes: 15:24 represented by 13 bits.
Honestly, I'd rather it represent the hours and minutes as individual blocks so the previous example would look like:
◻︎◼︎◼︎◼︎◼︎ = 15
◻︎◼︎◼︎◻︎◻︎◻︎ = 24
This is slightly better, down to 12 bits!
But this got me thinking, how many bits do we actually need?
Down the rabbit hole
My first thought was that the whole time could be a single number, the maximum number we need to represent is: 2359
This needs a max of: 12 bits, so we have our answer right?
Wrong! Becasuse there are a whole host of numbers between 0->2359 that are not valid times!
There are a total of: 60 * 24 = 1440 representable times.
We can represent all of these within 11 bits (with quite a bit of room to spare) so my next question was, is there a way to represent the times in a way that uses the minimum number of bits, such that it's still easy to read?
Thinking about where we are losing out space-wise in the two examples given above:
Watch face format:
- 2 bits for 0->2 (3/4 states used)
- 4 bits for 0->9 (10/16 states used)
- 3 bits for 0->5 (6/8 states used)
- 4 bits for 0->9 (10/16 states used)
My preferred format:
- 5 bits for 0->23 (24/32 states used)
- 6 bits for 0->59 (60/64 states used)
Let's try some stuff(TM)
Let's see what happens if we apply perhaps the most obvious optimisation... AM/PM. This evenly divides the data into half (at the cost of 1 bit) but allows us to forgoe everything upwards of 12 in the hours place.
The effect of this is we now need, for the watch face format:
◻︎ = 0
◻︎◻︎◼︎◼︎ = 3
◻︎◼︎ = 2
◻︎◼︎◻︎◼︎ = 4
◼︎ = PM
This has no effect on overall bit size unfortunately, the AM/PM bit is utilised to save us one bit in the hour, but this trade still results in 13 total bits.
The effect of this change on the preferred format is:
◻︎◻︎◼︎◼︎ = 03
◻︎◼︎◼︎◻︎◻︎◻︎ = 24
◼︎ = PM
Unfortunatelty this still doesn't quite get us there! We again can lose one bit on the hours in exchange for the AM/PM, but it doesn't matter, we still can't get down to 11!
Looking back at the stats from before, majority of the loss is coming from representing: 0-9 with 4 bits, or 0-23 as 5 bits, or 0->12 as 4 bits.
So my next idea was the following:
0->1 = 1 bit
AM/PM = 1 bit
_h:mm = 10 bits (0->959)
I won't subject you to how this looks, as it's not an improvement really, let's get back to thinking about this some more.
The biggest problem with the last example is that 60-100 is always included but unecessary (2/5ths of the states don't represent a valid time! e.g. x6x x7x x8x x9x) If we can limit this digit maybe it'll work...
Eureka!
__:m_ This m can be represented by 3 bits (0->5)
hh:_m This can be represented in 8 bits (0->239 can represent the rest)
So we did it!!!
Taking the example from before represented by our new (somewhat) easy to read watch face we get:
◻︎◼︎◻︎ = 2
◼︎◻︎◻︎◼︎◼︎◻︎◼︎◻︎ = 154
Which becomes: 15:24!
Analysing this as before we get:
- 3 bits for 0->6 (7/8 states used)
- 8 bits for 0->239 (240/256 states used)
Now you may complain that this isn't easy enough to read, but sometimes being optimal comes with a price. The feeling you get when you look at your wrist and know that you couldn't do better though... now that's priceless. Ultimately this was a fun exercise and an interesting thought experiment. I'd be interested to hear if anyone can come up with alternative representations that also use 11 bits (and maybe are a bit easier to read). Until next time (ha! get it?!)...
Check out an implementation of the clock here
- etopiei (29/06/2023)