How to shorten large numbers (an in-depth tutorial)

Daanny

Member
I did some looking around the internet for a guide on how to do this and there wasn't much coming up, so I thought I'd make my own tutorial so anyone else who needs this can go here.

GM Version: Studio (all)
Target Platform: all
Download: see tutorial below
Links: n/a

Summary:
How to display large numbers in an easily read text form.

What is a number abbreviation?
If you ever have a number being drawn on to the screen, but it's too long to understand and just looks like a big jumble of numbers, you should probably set up a number abbreviation system. Basically it's just a system that turns bigger numbers into much more readable blocks of text. For example if you have the number one billion, just drawing that number will look like this:

1000000000

Which is very annoying to read and it's hard to quickly identify as one billion. Abbreviating your numbers just means that for every power of 1,000 (thousands, millions, billions, etc) the game alters the number it's displaying to be a 1-3 digit number followed by the biggest power of 1,000. So the number one billion would, instead of being displayed as a one followed by a long line of zeros, would instead be displayed like:

1 billion
1 B
1 G
Or however else you wish to present the number.

How to do it
You can shorten your numbers through a single, custom-made function, which you input the number you want to shorten in to, and it will return your desired result. In this example I'm going to be using a points system, but this can be used for any numerical value you wish.

First you will want an object to declare the variable, it doesn't have to be global as it'll only be ran through a script. You will also need to create a second variable, which for this tutorial we will just declare as value. It isn't important what it's called as it will only be used in our script. I will go into detail on what this variable does once we start writing our function.

in obj_score create event:
GML:
points = 0;
value = 0;
The only other thing you need to do with your object is add a draw event and write this.

In obj_score draw event:
GML:
draw_text(x, y, shorten(points));
(Reminder that if you're doing this in an object that you want to have visible to add draw_self(); in your draw event)

Shorten
is the name of the function we are going to create, however you can name it something more fitting for what you want to use it for.

Now we move on to making the script, name it whatever you like but make sure that in the script you change the name of the function to whatever you put into your draw_text. So for this example it'll look like this:

In sc_shorten:
GML:
function shorten(){

}
The next thing we will need to set up is our argument, as we want to be able to input a number into this function. To do this you need to put free forward slashes and then @arg followed by whatever you want to name your argument. For simplicity sake I will just call this argument num:

In sr_shorten:
GML:
function shorten(){
/// @arg num - the number you wish to shorten
}
Now that we've set up our function and our argument, it's time to set up some variables. To save time I recommend naming these variables very short phrases, in this tutorial I am going to use the variables i, t, and the aforementioned value. So what are these 3 variables?

i - This is going to an integer value that determines what shortened number we are going to receive

t - This is going to be the text displayed next to our number (e.g. thousand, billion, trillion)

value - this is going to be the number displayed instead of our score variable, as if we just displayed our score variable we would have results like 1000 thousand or 1000000 million. Basically all it does is it gets rid of those unnecessary zeros at the end of our score variable by dividing by 1,000 for every time it exceeds 1,000 meaning it'll always be between 0 and 999, which instead gives us results like 1 thousand or 1 million.

Within the curly brackets of your function, you will want to declare the variables i and t, and set value to argument0:

In sr_shorten:
GML:
function shorten(){
/// @arg num - the number you wish to shorten

var i = log10(argument0);
var t = "";
value = (argument0);
}
Okay so there's a lot to unpack here. So starting with the easy part, we set t to be an empty string by putting nothing between the speech marks. You don't have to do this as it won't be displayed unless your number is greater than 1,000 in which case it will change the string accordingly, but it keeps it tidy to leave blank.

Now to explain the i variable. To put in very simple terms, by making i log10 of our number instead of directly making it our number, it means we can shorten our score to much higher numbers. This is because local variables seem to only go up to the 64-bit integer limit whereas other variables can go all the way up to 1e308(a SIGNIFICANTLY bigger number). This is also why we made the value variable as it isn't a local variable and therefore can exceed the 64-bit integer limit. So instead the way we use i to figure out how big the number is, is by using logs.

For those who don't know what a log is, basically all the log10 command is doing is it's getting our score value and looking at the base of the log (in this case 10) and it's going to give us the number we need to raise 10 to to get to our number. For example if our number was 10, we would need to raise 10 to 1 as 10 ^ 1 = 10, so this command would make i 1. If our number was 1000, i would be 3 as 10 ^ 3 = 1000. A simple way to see it is:

x = logy(z)

Can be rearranged to:

y ^ x = z

using the log means that we can easily figure out what value of i we will be looking for through this very simple equation:

i = number of digits - 1

this means that if we are checking for 1,000 we would check to see if i was greater than or equal to 3, as 1,000 has 4 digits, so i will equal 3.

Now that we've got that all sorted, let's write out the code to shorten a number to the thousands place:

In sr_shorten:
GML:
function shorten(){
/// @arg num - the number you want to shorten

var i = log10(argument0);
var t = "":
value = argument0;

if (i >= 3)
{
    value /= 1000;
    t = " thousand"
}
}


So what does this code do? Well, it checks for if our number is greater than 1,000 through whether i is greater than or equal to 3, like the example from earlier. If i is greater than or equal to 3, our value variable gets divided by 1000, so if our number was 1360, we would divide that by 1000 so our end result would be 1.36 thousand. It also changes our t variable to " thousand". The reason there is a space at the start of the string it because if there wasn't, our number would look like:

5thousand

instead of:

5 thousand


To add more numbers to this list, all you need to do is make more if statements coming off of the previous one. This does look incredibly hard to keep track of if you're shortening numbers with hundreds of digits, but there isn't really any other way to shorten hundreds of digits without writing hundreds of shortened variations.


At the end of your code, you will want the function to return the value of value followed by t. You will need to write two separate commands for this, as if t wasnt greater than 3 you don't need to return t. To return a shortened number, all you have to do is at the very end of the if (i >= 3) statement, add this line:


GML:
return string_format(value, 1, 0) + t;

If you're not familiar with the string_format function, the first argument is the value you want, the second is the spacing, so if your spacing is 5 it would look like:

5 thousand

Instead of:

5 thousand

So basically all it does is it adds spacing before your number. Keep it at 1 for no spacing. The last number is how many decimal places you want your number to go to, in this example I'm going to leave it at 0 but change it around to find a value which suits your needs. The + t at the end just adds the string t to the end of your number.


So what if the number is less than 1,000 and that string isn't returned? Well we write a different return function at the very very bottom of our code, and all we write is:


GML:
else return(value);

You can return argument0 if you like as if none of the code runs they should be the same number.

So this is the finalized code, I have added the if statements all the way up to the billions to show what it will look like:



In sr_shorten:
GML:
function shorten(){
/// @arg num - the number you want to shorten

var i = log10(argument0);
var t = "";
value = argument0;

if (i >= 3)
{
    value /= 1000;
    t = " thousand";
    if (i >= 6)
    {
        value /= 1000;
        t = " million";
        if (i >= 9)
        {
             value /= 1000;
             t = " billion";
        }
    }
return string_format(value, 1, 0) + t;
}
else return(value);
}


As you can see, to add a new number you just increase the if statement by 3, and rename t to whatever you want it to be called. It is the neatest way I could find to do this system, I'd love to hear other ways of going about this!

Well this was my first attempt at a tutorial so I'd like feedback, was there anything I could've done better, anything I over explained? I know I'm not the greatest at explaining things but I tried my best!
 
Last edited:

gnysek

Member
Wouldn't it return similar result ?
GML:
function shorten(num) {
    var th = ["", " thousand", " million", " billion", " trillion"];
    for(var i=0; i<array_length(th); i++) {
        if (num >= 1000) {
            num /= 1000;
        } else {
            return string_format(value, 1, 0) + th[i];
        }
    }
}
 

Daanny

Member
Wouldn't it return similar result ?
GML:
function shorten(num) {
    var th = ["", " thousand", " million", " billion", " trillion"];
    for(var i=0; i<array_length(th); i++) {
        if (num >= 1000) {
            num /= 1000;
        } else {
            return string_format(value, 1, 0) + th[i];
        }
    }
}
Ah damn I forgot arrays existed, either way I wanted to make a design which was easy to understand for people just starting out with gml so I probably wouldn't have done it your way for this tutorial. But now I'm just kicking myself for not thinking of that
 

gnysek

Member
That's how programming works, there's many ways to achieve same thing. Sometimes it's good to show more options, and show how to optimize them. There still may be better solutions than mine :D What's interesting, you shown how to use log10 function, and that's important lesson!
 

Appsurd

Member
Wouldn't it return similar result ?
GML:
function shorten(num) {
    var th = ["", " thousand", " million", " billion", " trillion"];
    for(var i=0; i<array_length(th); i++) {
        if (num >= 1000) {
            num /= 1000;
        } else {
            return string_format(value, 1, 0) + th[i];
        }
    }
}
Neat script! The advantage of this script over the tutorial is also that it's easily extendable to larger numbers (i.e. with more than 15 digits)
 

Daanny

Member
That's how programming works, there's many ways to achieve same thing. Sometimes it's good to show more options, and show how to optimize them. There still may be better solutions than mine :D What's interesting, you shown how to use log10 function, and that's important lesson!
Well after about 30 minutes of trying to get it right, I've finally managed to get a script to put numbers into scientific notation. The best part? It's only 6 lines of code:
GML:
function getValue(){
///@arg value
var i = floor(log10(argument0))
var value = argument0 / (power(10, i))
return string_format(value, 1, 2) + "e" + string(i);
}
 

hxyy

Member
Support conversion of decimals and negative numbers
GML:
function String_shorten(num) {
    if(num<1 && num>-1){
        var th=[" m", " μ", " n", " p", " f"," a"," z"," y"];num*=10
        var i = abs(floor(log10(abs(num)))) div 3
        num *= power(10, i*3+2)
    }else{
        var th    = ["", " K", " M", " G", " T", " P"," E"," Z"," Y"];
        var i = floor(log10(abs(num))) div 3
        num /= power(10,3*i)
    }
    return string_format(num, 1, 2) + th[i];
}
 
Top