Operations and performance of BigDecimal and BigInteger

In this section we look in a little more detail at some of the most important methods on BigDecimal and BigInteger, including an analysis of their performance.

As a general note, it is worth bearing in mind that a BigDecimal is essentially a wrapper around a BigInteger that "remembers where the decimal point is". Because of the way we are used to dealing with numbers, as humans, we may have to "think a bit more" when calculating with non-integers by hand compared to integers (e.g. we may have learnt the 7 times table by heart to speed up certain calculations, but probably not the .7 times table). But to a computer implementation, it is essentially no more effort to manipulate non-integers than it is integers, and so on the whole, methods on BigDecimal tend to perform similarly to analogous methods on BigInteger.

add() and subtract()

Both BigInteger and BigDecimal have add() and subtract() methods that take as parameters another instance of the same class. The main thing to remember when using these methods is that, as with all of the arithmetic methods in general, a new instance is returned:

public BigInteger add(BigInteger[] numbers) {
  BigInteger total = new BigInteger(0);
  for (BigInteger n : numbers) {
    total = total.add(n);
  }
  return total;
}

If you need to add a different type for some reason (e.g. adding a primitive int to a BigInteger, or adding a BigInteger to a BigDecimal), then you need to wrap the argument in the relevant type.

In terms of performance, these methods scale linearly as a function of the number of digits in the (larger) number being added.

multiply()

The multiply() method works in a similar way to the add() and subtract() method, creating a new instance of the relevant class that is the product of the number it is called on and that passed in as an argument.

Unlike addition and subtraction, the time required to perform a multiplication scales exponentially with the number of digits in the multiplicands: or more specifically, multiplying a number with n digits by a number with m digits takes in the order of nm time. Or put another way, doubling the number of digits of the numbers multiplied together means that the time taken will increase fourfold.

This performance behaviour comes from the standard JDK implementation, which uses a "naive" algorithm of cycling through every combination of digits to perform the multiplication, much as you would if calculating by hand. If you require faster multiplication of very large numbers (greater than a few thousand digits), then it is faster to implment a more sophisticated algorithm. On the next page, we look at the performance of the multiply() method and of a simple implementation of the more scalable Karatsuba's method.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.