Skip to Content
Course content

145: Working with Big Numbers: BigInteger

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I once worked on a project for a logistics company that tried to calculate the number of possible routing combinations for a set of deliveries. They were using a long for their calculations, and for a while, everything looked great. Then they tried to run a simulation for 25 stops, and suddenly the software started reporting negative numbers. The developers were baffled—how can you have a negative amount of route combinations?

Here is a simplified version of the kind of code they wrote:

public long CalculatePermutations(int n)
{
    long result = 1;
    for (int i = 1; i <= n; i++)
    {
        result *= i;
    }
    return result;
}

// This works fine for 20
Console.WriteLine(CalculatePermutations(20)); // 2432902008176640000

// This breaks spectacularly for 21
Console.WriteLine(CalculatePermutations(21)); // -4249290049465653248

The Mystery of the Negative Factorial

If you look at that output, you'll see the result for 21 is negative. This is a classic integer overflow. A long in C# is a 64-bit signed integer, which means its maximum value is $9,223,372,036,854,775,807$.

When we multiply 20! by 21, the resulting number is larger than that maximum. In C#, by default, integer arithmetic wraps around. The bit that represents the "sign" (positive or negative) gets flipped, and suddenly your massive positive number becomes a massive negative number. The code didn't crash—which is the worst part—it just gave a confidently wrong answer.

Scaling Up with System.Numerics

When you hit the ceiling of a long, you can't just move to a "longer" integer; there isn't one. Instead, you need BigInteger. This type is located in the System.Numerics namespace. Unlike int or long, which have a fixed size in memory, BigInteger grows dynamically to accommodate as many digits as your computer's RAM can handle.

Here is how I would rewrite that logic to handle numbers of any size:

using System.Numerics;

public BigInteger CalculatePermutations(int n)
{
    BigInteger result = 1;
    for (int i = 1; i <= n; i++)
    {
        result *= i;
    }
    return result;
}

// Now we can handle 21, or even 100, without breaking a sweat
Console.WriteLine(CalculatePermutations(21)); 
// Output: 51090942171709440000

Notice that the syntax for multiplication remains exactly the same. BigInteger overloads the standard arithmetic operators (+, -, *, /), so it feels just like working with a normal integer. I should mention, though, that BigInteger is slower than long because it's an object-like structure managing a heap-allocated array of digits rather than a simple value in a CPU register. Don't use it for every single number in your app—only when the math actually demands it.

Converting and Parsing

You'll often get your big numbers as strings (like from a JSON API or a text file). BigInteger makes this easy with BigInteger.Parse(). You can also cast smaller integers directly into a BigInteger without any special method calls.

string massiveNumberString = "123456789012345678901234567890";
BigInteger bigNum = BigInteger.Parse(massiveNumberString);

int smallNum = 500;
BigInteger combined = bigNum + smallNum; // Implicit conversion works here



📋 Practical Task

Exercise: Computing the Total Permutations of a Standard Deck

In combinatorics, the number of ways to arrange a standard deck of 52 playing cards is 52 factorial (52!). This number is far too large for any primitive integer type in C#.

Your task is to create a small program that calculates 52! using BigInteger.

  • Add the using System.Numerics; directive.
  • Implement a loop or a method that calculates the factorial of 52.
  • Print the final result to the console.
  • Verify that your result is a massive positive number (it should be 80 digits long, starting with 80658...).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.