How to Use BigInteger for Arbitrarily Large Numbers

Working with numbers in the .NET framework is generally pretty straightforward. Just add a new double or integer and use all the basic operations you’re used to, like addition and multiplication. This works fine until you need to use a value that’s bigger (or smaller) than the standard number data types. Thankfully, since .NET 4 there’s a datatype that can support arbitrarily large numbers: BigInteger.

Getting Set Up

To access BigInteger, add a reference to System.Numerics in your project. Then it’s just a case of adding an Imports statement (or a using statement if C# is your thing).

Initializing BigInteger

As BigInteger is a non-primitive datatype, it can be instantiated using it’s constructor. The constructor is overloaded and all of its signatures just take a single parameter: the value to initialize the BigInteger with. The possible datatypes for this parameter are:

  • Decimal
  • Double
  • Integer
  • Long
  • Single
  • UInteger
  • ULong
  • Byte()

At first glance it’s easy to spot that most of these are standard numeric types. A couple of the less frequently used types, UInteger and ULong, also make an appearance. These are unsigned 32bit and 64bit integers respectively.

So a quick example of how to initialise a BigInteger variable could be:

This is OK, but the whole point of a BigInteger is to work with numbers that are too large to fit in a standard numeric type. What if we want to initialise BigInteger with a number that is already too big for the standard types?

The solution comes from the last entry on the list of datatypes above: we can use a byte array.

Initializing BigInteger using a Byte Array

Using a byte array we can put any size of number into a BigInteger object. The most important thing is to understand the format of the array that BigInteger is expecting so we don’t put the wrong number in there.

There are two main rules to follow:

  1. The bytes need to be in little-endian order
  2. Two’s complement is used for negative numbers

Little endian ordering means that the bytes need to be in the array in smallest-to-largest order, which is kind of backwards compared with how numbers are normally written. Here’s an example:

Here, the values 0 to 255 are used to represent the values stored by each byte. The first byte, 16, represents the smallest units of this number. This byte array represents the number 466,378,899,991,238,928.

The fact that negative numbers are represented using two’s complement means that you need to take care when initialising all numbers to make sure a number isn’t accidently turned negative.

Using two’s complement, all numbers where the most significant bit (MSB) is 1 (in binary) will be treated as negative. In our example, this means that any byte array where the last value is 128 or over will be negative when used to initialise a BigInteger object.

Negative numbers run backwards compared to positive numbers so the smallest negative integer, –1, can be represented using the following byte array:

And –2 can be represented as:

Of course, this is just an example, you don’t need four bytes to represent –1 or –2 and you should pick a relevant number of bytes for your application.

Using BigInteger

After you BigInteger objects have been set up, all the standard mathematical operations are available and can be used like any other numerical type

For an in-depth look at all the operations, take a look at the BigInteger MSDN page.

Leave a Reply

Your e-mail address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.