Positional notation numbers without zero

· ddrake's blog

you can do positional notation numbers without a symbol for zero!

I learned learned about this from the late Terry Millar: https://math.wisc.edu/2019/03/12/in-memoriam-terry-millar/. It's an example of bijective numeration. (Thanks @OscarCunningham@mathstodon.xyz for the reference!)

If you have children, and are the kind of person reading this, you are likely interested in the intersection of those two things and found your child's mathematical development super interesting. In particular, learning to count is surprisingly subtle and complex.

There's a point where questions like "what comes after '19'? What comes after '20'? '21'?" are super hard questions whose answers are not obvious. But you, dear reader, are so experienced that you likely don't appreciate the depth and complexity implicit in those questions.

In this post I hope to yank the carpet out from your experience, put your instincts in a blender, shed new light on a familiar subject...pick your metaphor. :)

# Base Ten Numbers Without Zero

You don't need zero to make a weighted positional notation system!


Let's do base ten, where "ten" has the usual everyday meaning: it's the number of characters in the string "helloworld".

# The classic system

In the usual, classic system, you have these symbols:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

and you can use them to make any one of the infinitely many positive integers. Here are the first few integers using these symbols:

1, 2, 3, 4, 5, 6, 7, 8, 9.

For the next number, you start using multiple symbols at once: "10" means "one ten and zero ones". The position is important! "12" and "21" are different, and in something like 202, the first "2" symbol means something different than the second "2" symbol. (That's the positional part.)

# But you don't need zero

It seems like you need zero: after all, in 202, for the hundreds "2", you need something to "put it in its place", so to speak. But you have that 2 on the right representing two ones, so it seems like you need something to represent having zero tens quantities, right?

My (er, Terry's) new system doesn't have zero. My symbols are

1, 2, 3, 4, 5, 6, 7, 8, 9, T

where T represents a quantity one more than the quantity represented by "9"; the number of characters in the string "helloworld".

Numbers are now:

1, 2, 3, 4, 5, 6, 7, 8, 9, T, 11, 12,...,19.

Okay, so "19" means this many things:

xxxxx xxxxx xxxxx xxxx
^^^^^^^^^^^ ^^^^^ ^^^^
 T things     5    4

If I have one more thing above -- if that last group has five things -- I have T things. So the new quantity is:

xxxxx xxxxx xxxxx xxxxx
^^^^^^^^^^^ ^^^^^^^^^^^
 one T        T ones

Or, in other words: 1T. Let's keep going! The next quantity is

xxxxx xxxxx xxxxx xxxxx x
^^^^^^^^^^^^^^^^^^^^^^^ ^
       two tens         one 1

So that's "21". So:

19, 1T, 21, 22,..., 29, 2T, 31,...,39, 3T, 41,...

Okay, we're starting to get a feel for the pattern. Keep going! We get up to 99, which our instincts say is where two-character things end, but here we get a bit more:

99, 9T, T1, T2, ... T9, TT.

"9T" represents "one hundred": it's 9 tens, and one more ten. The next number is "one hundred and one", or: "ten tens and one one", or T1.

"TT" represents "T tens and T ones", or one hundred and ten. The next number is one hundred eleven, which we think of as: one hundred, one ten, and one one. Or, in other words -- er, symbols: 111.

T9, TT, 111, 112, 113, ...., 119, 11T.

Now we can just use the same pattern as in the usual system: our symbols now look like the length-two symbols but with a "1" prepended.

121, 122,...129, 12T, 131, 132,...199, 19T, 1T1.

Symbols like "2T" and "TT" seem inefficient, since the symbols are referring to numbers of tens: in "2T", you have two tens...and ten ones. But ten ones is one ten. So you can also say you have two tens and one ten, which seems redundant. And indeed it is; we're not after efficiency here. We're having fun.

Keeping going with the pattern of "write '1', then go through the sequence of two-character symbols", we have:

1T1, 1T2, ... , 1TT, 121, 122, ... 1TT.

"1TT" is: one hundred, T tens, and T ones". We can regroup the one hundred and T tens as two hundreds, and the T ones as "one ten". The next number is two hundred and eleven: two hundred, one ten, and one one, or 211. Ah, now the bigger pattern is becoming clearer and your intuition for this number system should be firming up.

1TT, 211, 212,...

and you go through the mantra of "2, then go through our two-character symbols", you get to 2TT, and use the above reasoning to get 311 as your next symbol.

Ultimately you get up to TTT: T hundreds, T tens, and T ones. The next number has T hundreds, T tens, and eleven ones, which we regroup as:



or: T111!

# Now let's do arithmetic!

You know the classic pencil-and-paper arithmetic algorithms that we inflict on kids. You likely find them easy to use, if tedious. Try some "T arithmetic" to put yourself back in their shoes: try 3T2 + 119. That is, do:

+ 119

Don't cheat and convert 3T2 to 402!

# Show me some code

You are likely the kind of person who at this point is thinking of just writing some code to do some conversions. Here's some Python -- I make no claims about efficiency or elegance:

def T_to_zero(Tnum):
    """Convert a number (provided as a string) written with "T" notation
    to a Python integer, which by default is displayed using the
    ordinary positional notation with "0".
    T_digit_to_int = {str(i): i for i in range(1, 10)}
    T_digit_to_int['T'] = 10
    return sum(10**p * T_digit_to_int[d] for p, d in enumerate(reversed(Tnum)))

def zero_to_T(x):
    """Convert a number (a positive Python int) into "T" notation."""
    if x == 0:
        return ""
        r = x % 10
        r_ = r if r > 0 else 10
        y = (x - r_) // 10
        int_to_T_digit = {i: str(i) for i in range(1, 10)}
        int_to_T_digit[0] = 'T'
        return zero_to_T(y) + int_to_T_digit[r]

I also made a gist if you desire syntax highlighting, pull requests, and so on. :)