If you are dealing with big numbers in your game, you might have encountered the issue of properly formatting them. Small numbers such as 1.000, 10.000 or even 100.000 are OK, but as you go larger, you’ll run out of space quickly. I mean, how can you fit 1.000.000.000.000.000.000 in a button?

But let’s say, you found a way. Let’s say you were able to put 1.000.000.000.000.000.000 in a button properly and it looks good (I can’t imagine how, but for the sake of the argument let’s assume it is possible). However, you still have the UX problem of clarity. No one will be able to see this number and say “oh my gosh, I have one quintillion cookies!”. I am a mathematician and even I can’t read it without counting zeros first.

There are two solutions to this problem and they both have pros and cons:

Scientific notation

Mostly used by scientists and engineers, this notation is formatted as follows:

m x 10n

Where m is a real number called coefficient, and n is an integer called mantissa. In order to make this more concrete, let’s have a look at some examples:

Number Scientific notation
1 1
1.000 1x103
5.000 5x103
1.250.000 1,25x106
1.254.678 1,254678x106
1.500.000.000 1,5x109

The good thing about this notation is that you can format any number. Moreover, by using only 2 (or as much as you want) digits after the decimal point, you can make your numbers fit in a limited amount of space.

The problem is that this notation is used by scientists and engineers (as I mentioned earlier), and it will not make any sense to most people. Even if it does, superscripts (I mean this) would look really tiny on mobile screens and would cause minor UX problems.

Single letter notation

There is no science backing up this notation, it’s something purely linguistic: single letter after the number. This is the notation you use in your daily life.

Number Single letter notation
1 1
1.000 1k
5.000 5k
1.250.000 1,25m
1.254.678 1,254678m
1.500.000.000 1,5x10b

Since it’s already used by everyone, this notation has the advantage of being accessible. Almost everyone can tell 1.25m means one million two hundred fifty thousand. However, not many people would know that q in 1.25q stands for quadrillion and it becomes more of an issue as you go larger. See for yourself: quintillion, sextillion, septillion, octillion, nonillion. Do you see anything familiar? Probably no, because these units are not used in daily life unless you are a scientist or a mathematician. We need something more intuitive.

The “aa” notation

A better solution is the combination of the two: single letter notation up until (but not including) quadrillion and a two letter representation of scientific notation after trillion. It may sound confusing, but you’ll agree that it’s actually rather clear when you see it in action:

Number Written Scientific Single letter “aa”
1 one 1 1 1
1.000 one thousand 1x103 1k 1k
1.000.000 one million 1x106 1m 1m
1.000.000.000 one billion 1x109 1b 1b
1.000.000.000.000 one trillion 1x1012 1t 1t
1.000.000.000.000.000 one quadrillion 1x1015 1q 1aa
1.000.000.000.000.000.000 one quintillion 1x1018 1? 1ab

So, one quadrillion (1015) will be represented with aa. For every power of 1000 we’ll move the second letter up, so one quintillion (1018) will be ab, one sextillion will be ac, and so on. After az, we’ll move to ba, bb, bc etc. This algorithm provides an incremental representation of big numbers and it does not require any scientific or mathematical knowledge. The algorithm for this notation is rather simple and it’s really easy to implement in any programming language.

First we need to represent the number in a slightly modified scientific notation. Since we change the unit for every power of 1000 we’ll convert our number to this: m x 1000n . In order to find m and n, all we have to do is revert the formula and apply it:

n = (int) log(value, 1000);
m = value / pow(1000, n);

If n is less than 5 (i.e the number is less than 1x1015), we will use the single letter notation (K, M, B, or T). If it is greater than or equal to 5, we’ll convert this number to “aa” notation. So 5 (1015) will be aa, 6 (1018) will be ab, 7 (1021) will be ac and so on. Since there are 26 letters in English language, converting n to a two letter representation would require one modulo and one division operations:

secondUnit = n % 26; 
firstUnit = n / 26; 
unit = firstUnit.toChar() + secondUnit.toChar();

That’s all there is to it. And here is the implementation in C#:

public static class CalcUtils
{
    private static readonly int charA = Convert.ToInt32('a');

    private static readonly Dictionary<int, string> units = new Dictionary<int, string>
    {
        {0, ""},
        {1, "K"},
        {2, "M"},
        {3, "B"},
        {4, "T"}
    };

    public static string FormatNumber(double value)
    {
        if (value < 1d)
        {
            return "0";
        }

        var n = (int) Math.Log(value, 1000);
        var m = value / Math.Pow(1000, n);
        var unit = "";

        if (n < units.Count)
        {
            unit = units[n];
        }
        else
        {
            var unitInt = n - units.Count;
            var secondUnit = unitInt % 26;
            var firstUnit = unitInt / 26;
            unit = Convert.ToChar(firstUnit + charA).ToString() + Convert.ToChar(secondUnit + charA).ToString();
        }

        // Math.Floor(m * 100) / 100) fixes rounding errors
        return (Math.Floor(m * 100) / 100).ToString("0.##") + unit;
    }
}