C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
145: Working with Big Numbers: BigInteger
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...).
There are no comments for now.