38
\$\begingroup\$

Just wondering how games such as Tap titans and Cookie Clicker handle such large numbers.

I am trying to implement an idle game however the largest number format supported by C# is decimal.

I am looking to support up to 10^500; that needs to be floating point

How could I handle this?

P.S. it needs to be cross platform i.e. pc, mac, ios, android and compatible with Unity

\$\endgroup\$
6
  • 2
    \$\begingroup\$ If I remember correctly, you have access to the code of Cookie Clicker, so you could just check that... \$\endgroup\$
    – Vaillancourt
    Jan 15, 2016 at 12:30
  • \$\begingroup\$ No, I had already blocked Cookie Clicker from my mind! \$\endgroup\$ Jan 15, 2016 at 15:30
  • 2
    \$\begingroup\$ I've looked at the decompiled source for TimeClickers, a Unity game. They just use double. If it had been me, I would have used BigInteger, though. \$\endgroup\$ Jan 15, 2016 at 16:33
  • 1
    \$\begingroup\$ @VioletGiraffe: I kind of agree, and I would like to make my (our?) doubts concrete (to possibly help the OP make the question clearer): What is an "idle game" with respect to the question (that makes this kind of game a special case for large numbers)? What kinds of numbers is "such large numbers" (highlight by myself) referring to? How large do you want numbers to be? \$\endgroup\$ Jan 16, 2016 at 14:23
  • \$\begingroup\$ you can always store such a large number in a string and easily implement a function to do summation on string. \$\endgroup\$
    – dev-masih
    Jan 17, 2016 at 11:24

7 Answers 7

45
\$\begingroup\$

If you just want to store massive numbers without complete accuracy, for example if you're going to show 12,567,000,000,000 as 12.567T (trillion), you could just use standard float/decimal values and show the first x significant figures, with a suitable suffix like this. When you're in the octillions, do you really need to care about each individual integer increment?

\$\endgroup\$
7
  • 6
    \$\begingroup\$ This is probably the most sensible answer really, and I'm fairly sure Cookie Clicker just uses a standard float in JavaScript - case in point when I "hacked" it (read: messed with the console) and when the number reached 1e308 it jumped to "Infinity" and I could proceed to buy anything I wanted XD \$\endgroup\$ Jan 15, 2016 at 14:27
  • 1
    \$\begingroup\$ Great! The fact that the text jumps to "Infinity" feels like the developer was defensively coding around this eventuality too. Also, simplicity is king. \$\endgroup\$ Jan 15, 2016 at 15:58
  • 21
    \$\begingroup\$ @RossTurner - There was probably no coding involved in having it show the text "Infinity" -- infinity's just a value a float can have, and JavaScript knows how to display it, like many other languages do. \$\endgroup\$
    – Jibb Smart
    Jan 15, 2016 at 16:11
  • \$\begingroup\$ Standard floats (double precision, 8 bytes) will not allow to represent numbers up to 10^500 as the original question requires, it will go up to ~ 10^300. If that is not enough, one should use quad precision floats which may be nontrivial depending on the language. \$\endgroup\$
    – Peteris
    Jan 15, 2016 at 18:20
  • 2
    \$\begingroup\$ What every programmer should know about floating point numbers: floating-point-gui.de \$\endgroup\$
    – Steve
    Jan 15, 2016 at 19:17
23
\$\begingroup\$

You could use something like BigInteger, which is only available in .net 4.0 if I am not mistaken (not supported by all unity build platforms).

There are some libraries that attempt to bring this functionality without the requirement of .net 4.0 however. For example here.

Alternatively, you can use smaller numbers to represent a larger one, by keeping track of the multiplier. For example:

double value = 9;
enum Multiplier {
   Hundred,
   Thousand,
   Million,
   Billion,
   Trillion
}

Now, even though you have a value of 9, if you couple it with your multiplier, you can effectively represent that 9 as 9 trillion (Either by putting in "trillion" or writing something that will append the zeros at the end of your value).

\$\endgroup\$
2
  • \$\begingroup\$ It is worth noting that classes such as BigInteger use a String or Byte[] representation of the number - so in effect one could create their own class which just adds and subtracts two "numbers as Strings or Bytes" - (usually this type of game doesn't need it but you could support multiplication or division or other more advanced functions as well) - but they'd have to keep in mind that there is always a physical limit where they may run out of memory. \$\endgroup\$ Jan 15, 2016 at 17:40
  • 2
    \$\begingroup\$ Each byte multiplies the size of your number by 256. If you run out of memory trying to represent a number, your number didn't have any real value anyway. 256^(2 billion) = 2^8^(2 billion) = 2^(16 billion) ~= 10^(5 billion) (wolframalpha broke trying to make that last conversion). You can specify individual Planck volumes in the observable universe using like 150 digits (<500 bits). \$\endgroup\$
    – MichaelS
    Jan 15, 2016 at 22:22
14
\$\begingroup\$

For handling big numbers, I'd look at what I think is a good example like Tower of Hero. Top left corner:

1

2
(source: mzstatic.com)

Without getting into gameplay, the way it handles numbers is relatively simple: You see two buckets of numbers. As you get higher in the tower, and make more "gold", the two buckets simply represent larger numbers.

120 
120M320K - 120 Million
120B631M - 120 Billion
120T134B - 120 Trillion

Once the game passes T it moves into a, b, c... z, aa, ab, ...

56aa608z

Doing it this way, it still lets you know how much gold you've "earned"... while not bogging the game down in details.

Do you really care about Millions when your number is past Trillions?

Does it keep the number in Int, Big Int, Float, Double, Decimal, ...? Custom Array? When you are handling numbers in such a "fuzzy" way, I don't think it matters...

All that that likely matters are the most significant parts - in this case, the first 6... After that, MAYBE the next 3 or 6 - since earning a few hundred K can roll over into Millions - but there gets a point where earning a few hundred K isn't going to affect you when you hit T... much less aa and beyond.

Your mileage will vary (depending on what you want/need)... Just thought I'd put out my 2c on what I think is a good/simple example.

Edit:

Further thoughts on how I would implement the numbering system: I'd have a number with 3 significant parts: XXXX.YYY(...)xZZZ.

X is the most significant digits, 
Y trailing 
Z the multiplier (multiple of 3).

So 120.365x1 would be 120k365... 120.365x2 would be 120M365K... etc. Hit the 4 leading (1200.365x2), then just rotate the numbers 1.200365(...)x3. Bam. You have 1B200M.

X.Y would fit easily in a Decimal or Float... with Z sitting next to it as an int/unsigned int.

With a float, you'd be able to keep a sizable - but increasingly unimportant - number of digits after the dot.

Z would the easily represent an easily understandable block of numbers:

K = 1
M = 2
B = 3
T = 4
a = 5 
...
z = 31 (I may be off on this)
aa = 32
...
az = 58
ba = 59
...
...
\$\endgroup\$
0
12
\$\begingroup\$

You'd likely need to write your own class for use in Unity, but it wouldn't be particularly hard.

Internally, it could be a list of integers (such as a List<int>), with each element in the list corresponding to a group of 9 digits. Each integer would have a range of 0 to 999 999 999. An integer can support a little over 2 billion, and double that if it's unsigned, but you'll want to have each "digit" overflow at 1 000 000 000, because you'll also want to be able to easily convert your big-number into a string for display. It's easier to concatenate what are already decimal digits (return group[i].ToString() + group[i-1].ToString() and so on) than to figure out how to display the sum of groups that are outside of the range of any regular data type.

So, the first int in your list would represent 1s. The next would be the number of billions. The next, the number of quadrillions, and so on.

Addition and subtraction would work just like pen-and-paper addition and subtraction, where you have to watch for overflow and carry it over to the next "digit", but instead of digits with a range of 0-9, your digits range from 0 to 999 999 999.

\$\endgroup\$
4
\$\begingroup\$

Use break_infinity.cs for unity: https://github.com/Razenpok/BreakInfinity.cs call using BreakInfinity; at the top of your script. If you want to save the variables (BigDouble), convert them to strings (saveStringVariable = bigDoubleNum.ToString()) and load the back, do (bigDoubleNum = BigDouble.Parse(saveStringVariable))

EDIT: It limits up to 1e(9e15), so yeah, you have a lot of room to work with. For people who are wondering, why? Check out Antimatter Dimensions (NG+3 especially) and https://lngi-incremental.glitch.me/, this one gets even bigger numbers than breakinfinity.cs

\$\endgroup\$
3
\$\begingroup\$

And easy way to handle large numbers is simply to have more than one INTEGER value then CARRY any overflow. If you have a 16-Bit INT value (0 to 65535) and you want to have more than that, use two 16-bit INT values in a row. Think of it like having a BYTE value (0 to 255) but only using it up to 99 digits of value. Once it hits 100, then roll it over to the next higher BYTE value for as many digits as you find useful. With today's GHZ computers, even sloppy coding like that is fine.

Of course, there is an alternative which is a bit faster.
If you're adding 18 repeatedly to a number, it is quicker to simply subtract 2 and add 20. 18, 36, 54, 72, 90, 108,... 18 = 20+(-2).
It works in Binary too. (Same decimal values in Binary) 10010, 100100, 110110, 1001000
DEC(18)=BIN(10010)
Except for easier Binary parsing, you need to think of 18 = 16+2
DEC(16 + 2)=BIN(10000 + 00010). If the value was 15, then think of it as adding 16 in binary and subtracting 1 (10000-00001).

In this way, you can keep the number chunks to manageable limits per value.
If you're using the sloppy coding method of limiting a basic 16-bit INT value (0 to 65535) to a 4-digit decimal limit (0 to 9999) then all you have to do is when the value exceeds the 9999 limit, subtract 9999 from it and carry it to the next value chunk (since you are doing basically "adds & subs" with numbers versus a real binary computation).

Ideally, you'd just use SYMBOLIC MATH that you learned in grade school. How does this work. If you have the decimal symbol of 1 and you add it to 1 then you get the decimal symbol of 2. The reason I call this a symbol is that you could use any symbol series with a lookup table of "IF symbol X(1) is added to symbol X(4) THEN output symbol X(5)". Symbol X(1) could be a picture of a cat and symbol X(4) could be the PERCENT sign, but it doesn't matter. You have your symbols, a basic rulebook of what happens then those symbols are combined (sort of like the multiplication tables you memorized as a kid) and what symbol must result as a part of that operation. With using Symbolic Math you can add an infinite number of digits while never really calling the numeric limits of your processor.

One way to do this in easy coding is to have each represented digit you wish to work with as a single unit in a large dimensioned array. If you want to represent 4096 decimal digits (not exceeding 2 digits per unit box), then you allocate 2 sets of 4096 BYTE array locations. Storing the value of 1098874 would utilize the array as (1)(0)(9)(8)(8)(7)(4). If you added the value of 7756 to it, you would convert it into (7)(7)(5)(6) and then add. The result would be (1)(0)(9)(15)(15)(12)(10) which you would then right-to-left subtract shift until all digit boxes were normalized to be the value of (0 to 9). The rightmost (10) value would have 10 subtracted and the resulting value would be zero (0) and that would carry the (1) value to the next-left box of (12) to make it (13) which then would have 10 subtracted to make it into (3). And so on until you had your result of (1)(1)(0)(6)(6)(3)(0) = 1106630.

\$\endgroup\$
3
  • 7
    \$\begingroup\$ -1 for TOO MUCH YELLING \$\endgroup\$ Jan 17, 2016 at 20:22
  • 6
    \$\begingroup\$ Hey @Gridlock, I just noticed that you're a completely new user— welcome. I should clarify that my downvote is not permanent; it's intended as constructive criticism. There's value to the answer you've provided, and I'll be more than happy to change it to an upvote once you've made the correction I'm requesting. Also, keep in mind that SE isn't a forum; a good answer isn't read and then forgotten but rather pays rep dividends over time. Revising your answers here & there helps the community and your rep. I hope I didn't scare you away; again, welcome and I hope you'll use my advice. \$\endgroup\$ Jan 18, 2016 at 16:34
  • 2
    \$\begingroup\$ The one other suggestion I'll offer is using some basic Markdown formatting instead of all-caps. Wrapping text in backticks (`text`text) will make it mono-spaced, appropriate for chunks of code, function names, data, etc. Wrapping text in underscores or asterisks will make it italic (_text_ or *text*text), and double-underscores or double-asterisks will make it bold (__text__ or **text**text). \$\endgroup\$ Jan 18, 2016 at 16:42
2
\$\begingroup\$

What you do is have several variables combined, and then you control the addition and overflow between them yourself. You can have any number of digits that way. Combine 10,000 32-bit integers, and you have a number with 32,000 bits, if that is what you need. There is no limit in any programming language. The only limit is what you are able to figure out.

\$\endgroup\$
2
  • \$\begingroup\$ For those that gave downvotes, could you explain why? Even if this is not a popular solution, or one that requires zero effort, it's still a solution, and one that could allow you to do the same even on languages/platforms that do not support double or BigInteger. \$\endgroup\$ Mar 19, 2019 at 17:21
  • \$\begingroup\$ @TomTsagk Possibly because there is already an older and higher voted answer that expands more about this same idea \$\endgroup\$
    – Aistis
    Nov 8, 2023 at 10:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .