Formatting Big Numbers: The aa Notation
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;
}
}