What is a BigInteger and how to use it in Java

A BigInteger is a data structure in Java that is used to represent very large numerical values that would otherwise not fit within a primitive data type such as an int or long. In this post, we will discuss different ways to initialize a BigInteger and how to use it to perform mathematical operations.

Why do we need BigIntegers

Sometimes primitive data types are not large enough to store calculated values. The largest primitive data type that can store integer values in Java is the 64-bit long. Given that it is a signed data type, this gives it the range from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

So we already established that BigIntegers are “Big”. But, what would we need such a data structure for. Well, there are some applications that come to mind. For example, if you are developing an application for Astronomy, trying to calculate the number of comets in a Galaxy, or trying to perform a calculation involving the 1,000,000,000,000,000,000,000 stars that exist in the universe.

Or if you are developing an application for physicists who would like to calculate the weight of a start. For example, our Sun is estimated to weigh 2 x 1030 Kilograms. Big integers would definitely come in handy in such situations.

How to initialize a BigInteger

There are multiple ways to initialize a BigInteger in Java. The way you initialize it depend on where you get your data from. Therefore, we will explore a few options that might be useful.

Initializing a BigInteger from a primitive (byte/short/int/long)

In order to create an instance of a BigInteger from primitive data types such as a byte, short, int or a long, you will need to use the method BigInteger.valueOf(primitiveValue). Let us use try this with a simple program:

    public static void main(String[] args) {

        byte byteValue = 22;
        short shortValue = 33;
        int intValue = 123456;
        long longValue = 123456789;

        BigInteger bigIntegerFromByte = BigInteger.valueOf(byteValue);
        BigInteger bigIntegerFromShort = BigInteger.valueOf(shortValue);
        BigInteger bigIntegerFromInt = BigInteger.valueOf(intValue);
        BigInteger bigIntegerFromLong = BigInteger.valueOf(longValue);
   
        System.out.println(bigIntegerFromByte);
        System.out.println(bigIntegerFromShort);
        System.out.println(bigIntegerFromInt);
        System.out.println(bigIntegerFromLong);
    }

Running this program will yield the following results:

22
33
123456
123456789

So what if we need to create a BigInteger with a very large value. We cannot store the very large value into a primitive first. And if we write the very large value directly into the valueOf method, we would get a compile error as the number is too large. Check the following code snippet for example:

        BigInteger bigInteger1 = BigInteger.valueOf(12345);  //this is ok
        BigInteger bigInteger2 = BigInteger.valueOf(100000000000000000000); //compile error

To solve this issue, let us go to our next section

Initializing a BigInteger from a String

In order to create a BigInteger instance from a String, all you need to do is to feed the String representation of your number to the BigInteger constructor. Please check the next code snippet as an example:

        BigInteger bigInteger = new BigInteger("10000000000000000000000000000000");

        System.out.println("Value is: " +bigInteger);

Running this program will yield the following result on the console output:

Value is: 10000000000000000000000000000000

Now that we are able to represent numerical numbers using Strings, we have raised the maximum number we can initialize a big integer to a number with 2147483647 digits. This is because the maximum length of a String is Integer.MAX_VALUE.

Initializing a BigInteger from a byte array

A big integer can also be intialized from a byte array. Simply feed your array to the constructor in order to get an instance of the big integer with the desired value. Please note that you need to represent your number in a big endian annotation. This means that the most significant data should come at the beginning of the array, and the least significant bits should be at the end of the array. Let us try it with an example:

        BigInteger bigInteger = new BigInteger(new byte[]{0x1F, 0x00});

        System.out.println("Value is: " +bigInteger);

The output of the program will be:

Value is: 7936

Also note that the BigInteger library assumes a two’s complement binary representation of the number. This means that you can represent negative numbers with the array, if you mark the most significant byte as negative. Our limitation here is the maximum size of the byte array, which is the same as the maximum value of an integer.

So what if your data is stored in a file, how can we read BigIntegers from a file?

Initializing a BigInteger from a file

If you decide to store the values of your very large numbers in a file, then you can easily read those numbers in Java. You can easily create a new instance of a big integer with a value read from a file using a Scanner. Let us create a file somewhere in our file system with the following content.

821938901283908190238903218903128093278123789123789123798

Now, let us read the file from Java. All we need to do to read the value is to call the method scanner.nextBigInteger()

    public static void main(String[] args) throws FileNotFoundException {

        File file = new File("C:/test/mybignumber.txt");

        Scanner scanner = new Scanner(file);

        BigInteger bigInteger = scanner.nextBigInteger();

        System.out.println("Value is: " +bigInteger);
    }

Running this program will give us the following result.

Value is: 821938901283908190238903218903128093278123789123789123798

Arithmetic operations with BigInteger

Arithmetic operations on BigIntegers are done through the functions provided by the BigInteger library. Given two big integer instances a and b, one can perform the following arithmetic operations as follows:

  • Addition (a + b): a.add(b)
  • Subtraction (a – b): a.subract(b)
  • Division (a / b): a.divide(b)
  • Multiplication (a * b): a.multiply(b)
  • Modulus (a % b): a.mod(b)
  • XOR (a ^ b): a.xor(b)

Let us try out these functions via a Java program.

        BigInteger a = BigInteger.valueOf(12345);
        BigInteger b = BigInteger.valueOf(45);

        BigInteger c = a.add(b);  // equivalent to a + b
        BigInteger d = a.subtract(b); // equivalent to a - b
        BigInteger e = a.divide(b); // equivalent to a / b
        BigInteger f = a.multiply(b); // equivalent to a * b

        BigInteger g = a.mod(b); // equivalent to a % b
        BigInteger h = a.xor(b); // equivalent to a ^ b

        System.out.println("Value of c : " +c);
        System.out.println("Value of d : " +d);
        System.out.println("Value of e : " +e);
        System.out.println("Value of f : " +f);
        System.out.println("Value of g : " +g);
        System.out.println("Value of h : " +h);

Running this program will produce the following result:

Value of c : 12390
Value of d : 12300
Value of e : 274
Value of f : 555525
Value of g : 15
Value of h : 12308

Please also note that the BigInteger class has a lot of other functionalities, such as a.abs() which produces the absolute value (non-negative value) of the given number a. Or the function a.gcd(b) which finds the greatest common divisor of the two numbers.

BigIntegers are Immutable

Notice that each time you perform an arithmetic operation, a new BigInteger instance is produced. This is because already instantiated instances of BigIntegers are immutable. In other words, once you have created an instance, you cannot change the value of that instance. One can only assume that this was done by the Java creators because it was simpler to implement and less error prone, However, this also comes at the cost of memory as for each new instance, a new place in the JVM memory is reserved.

What is the maximum size of a BigInteger in Java

The officially supported range of values for Big integers is -(2Integer.MAX_VALUE) -1 to +(2Integer.MAX_VALUE) -1 . However, larger values are theoretically possible and are technically limited to the amount of memory the Java virtual machine has. You can read more about this in the official documentation.

In other words, there is official support for the given range of numbers in BigIntegers, however, you can unofficially use it for even larger numbers if you have enough computing resources for it.

Summary

In this post, we discussed different methods of creating instances of BigIntegers. We discussed why they are necessary and we discussed how to perform basic arithmetic operations on them. Finally, we discussed the limitations of BigIntegers in regards to their immutability and the maximum size of the numbers they can store.

If you liked this post, them please make sure to follow us on twitter by clicking the follow button down below 🙂