BigDecimal and BigInteger: calculations with arbitrary precision in Java
For most everyday applications, even including various engineering applications,
standard floating point calculations
using the double type1, along with the utility methods
of Java's java.util.Math class are
sufficient for performing basic
mathematical operations in Java. However, there are some cases where they
are not sufficient:
- where numbers are required to have a large or arbitrary range (smaller
than approx 1x10-307 or larger than approx 1x10307 which
are the limits of the double type);
- where numbers are required to have arbitrary precision (e.g. more
than the 15 or so significant decimal figures that you get with a double);
- where we need to be able to represent decimal numbers precisely.
A notable area where arbitrarily large numbers are required is in
various cryptographic algorithms (typically asymetric algorithms such as
RSA). The last point
is slightly more subtle but illustrated if we perform a calculation such
as the following:
double pence = 0.10d;
double total = pence * 3d;
We might expect this to output the answer 0.3, but what we actually get is:
The tiny error that appears is caused by the fact that the number 0.1 cannot be
represented precisely in binary floating-point notation (i.e. in the representation
used by float and double). Conversely, if pence is changed
to 0.25, 0.50 or 0.75 (numbers which can be represented precisely because they
consist of sums of powers of two) then the precise answer is printed.
In certain applications, we might not want arbitrary errors to accumulate or not
depending on whether the amount of pence happens to hit on particular values.
BigInteger vs BigDecimal: working with numbers with a large/arbitrary number of digits
Java provides a couple of classes in the java.math package to alleviate
the limitations mentioned above:
- the BigInteger class allows representation of and calculations
on arbitrarily large integers (whole numbers);
- the BigDecimal class allows precise representation of any real number
that can be represented precisely in decimal notation, and allows calculations
on non-integers with arbitrary precision.
It's important to note that "arbitrary" precision or number of digits does not mean "unlimited": it means
that the number of digits in a number or number of digits of precision in a calculation
is limited by memory and/or defined limits to precision that we specify. In simple cases,
we use BigInteger and BigDecimal was follows:
- we construct a number by passing a string representation of the decimal
BigDecimal amount = new BigDecimal("6.57");
- we call methods such as add(), multiply(), subtract(),
- each of these methods returns a new instance of BigInteger or
BigInteger number1 = ...
BigInteger number2 = ...
BigInteger sum = number1.add(number2);
Next: overview of operations
In the next section, we present an overview of methods for performing
operations on BigDecimal and BigInteger
numbers, along with information about their performance.
1. On the other hand, as mentioned previously, the float type offers quite
poor precision and is best avoided.