In this tutorial, we will discuss how to use the Java BigDecimal class to deal with calculations that require a high degree of precision, such as when dealing with currency conversion, taxes or even high accuracy mathematical calculations.
Why do we need the BigDecimal class
In Java a floating point or a fractional number can be stored using two types, doubles and floats. Both primitive data types have a limited size of 64 and 32 bit respectively. Having a limited bit length also means that these data types have a limited accuracy.
Consider the following task: You are now a mathematical scientist hired by NASA and your first job is to calculate the surface area of a spherical object up to 20 decimal places. The object has a radius of 123212 meters.
After reading the task above, we immediately discover a problem. In order to calculate the surface area of the object up to 20 decimal places, we would have to use the value of pi with an accuracy of 20 decimal places. Let us attempt to do this in Java using the double data type.
public class BigDecimalExample1 { public static void main(String[]args){ double pi = 3.14159265358979323846; System.out.println(pi); } }
When we try to print the value of pi, we get the following value.
3.141592653589793
As we can see, even though we used the double data type, the last 5 decimal places got discarded. This is because of the limitations that the primitive data type “double” has and this is where a BigDecimal object comes in.
What is a BigDecimal
A BigDecimal is a data structure that is used to store floating point or fractional numbers with a high degree of accuracy. Such a data structure is important for developing several computer applications such as:
- Scientific and astronomical calculations
- Banking applications
- Statistics
- Stock market or commodity trading applications to name a few
Now that we know why the BigDecimal data structure is so important in Java, let us return to our previous example. We will start by first defining the value ofpi using BigDecimal.
import java.math.BigDecimal; //do not forget the import statement public class BigDecimalExample { public static void main(String[]args){ BigDecimal pi = new BigDecimal("3.14159265358979323846"); System.out.println(pi); } }
In the program above, we instantiated a BigDecimal by using a String representation of the floating point number. Even though BigDecimal offers constructors with floats, integers, doubles, etc.. non of them (other than another BigDecimal) can hold the value of pi up to 20 decimal places.
Let us run the program and find how pi will be printed.
3.14159265358979323846
Notice how the number was printed completely, with no loss of precision. The next step would be to calculate the surface area of the spherical object. It should be noted that since BigDecimals are objects, we cannot use the arithmetic operations ” * + – / …”, instead, we would be using BigDecimal’s own “.add(BigDecimal bd)” , “.subtract(..)“, “.multiply(….)” and so on. Also, since a BigDecimal is an object, mathematical calculations can only be done with other BigDecimal objects (we cannot add a byte to a big decimal using +, for example).
import java.math.BigDecimal; //do not forget the import statement public class BigDecimalExample2; { public static void main;(String[]args){ //Surface area = 4*pi*r^2 BigDecimal; pi; = new BigDecimal;("3.14159265358979323846"); //pi BigDecimal; radius; = new BigDecimal;(123212); // r radius; = radius.multiply;(radius); // r = r*r = r^2 radius; = radius.multiply;(new BigDecimal;(4)); // 4*r^2 BigDecimal; surfaceArea; = radius.multiply;(pi);// 4*pi*r^2; System.out.println;("surface area = "+surfaceArea); } }
Notice in the code how we instantiated a big decimal using an integer. Also notice that when we called the multiply method, we saved the result into the radius variable. This is because calling radius.multiply(xxxxx) does not change the value of either the object radius, or the method arguments, but rather the method returns the result of the multiplication into a new object which we assign to radius.
Running the program will result in the following output.
surface area = 190772547167.88087896520326106496
Limiting a BigDecimal to a certain degree of precision
Let us say, we would like to calculate something, up to 3 decimal places, no more and no less, and independent of the number we used to instantiate the BigDecimal. In that case, we will have to specify two properties of a BigDecimal object. The rounding mode and the scale. The scale defines the number of decimal places we want, and the rounding mode specifies how we would like to round a floating point number.
To illustrate, we will modify the our example as follows : “After you have created the program, your bosses at NASA have changed their minds, and they now want the result to be accurate up to no more than 3 decimal places. You are now required to change your program as we have run out of ink.”
To do so, we will use the “.setScale(…)” method just before the printing method with a scale value of 3 and a rounding mode of ROUND_CEILING. This will round up the number to the third decimal place.
surfaceArea = surfaceArea.setScale(3, BigDecimal.ROUND_CEILING); System.out.println("surface area = "+surfaceArea);
The statement we added limited the number of decimal places to 3. In general, it is good practice to set a rounding mode for any big decimal object or one could risk an arithmetic exception being thrown as shown here. Now, after running the program, we receive the following output.
surface area = 190772547167.881
Notice how the number is now limited to 3 decimal places. Also notice that the decimal part is “.881”, this is because we defined a rounding mode of “ceiling”, where a decimal place is rounded up even if the next decimal place is low or zero.
Comparing BigDecimals
As with primitive numeric data types, one will sometimes need to compare BigDecimals with each other. To do so, one will have to use the “x.compareTo( BigDecimal bd)” method. (Note: Do not use the .equals() method. It is a common mistake to do so, as it compares two pointers to determine if they point to the same object, and has nothing to do with numeric equality). The compareTo method works as follows: let us say we make the call “x.compareTo(y)”. The result of this call will be as follows:
-1 : if x is less than y
0 : if x is equal to y
+1: if x more than y
To illustrate, consider the following code, and it’s corresponding output:
import java.math.BigDecimal //do not forget the import statement public class BigDecimalExample3 { public static void main;(String[]args){ BigDecimal a = new BigDecimal;("3"); BigDecimal b = new BigDecimal("3.00"); BigDecimal c = new BigDecimal("-2"); System.out.println("a compared to b = "+a.compareTo(b)); //0 System.out.println("a compared to c = "+a.compareTo(c)); //1 System.out.println("b compared to a = "+b.compareTo(a)); //0 System.out.println("b compared to c = "+b.compareTo(c)); //1 System.out.println("c compared to a = "+c.compareTo(a)); //-1 System.out.println("c compared to b = "+c.compareTo(b)); //-1 } }
Output:
a compared to b = 0 a compared to c = 1 b compared to a = 0 b compared to c = 1 c compared to a = -1 c compared to b = -1
Finally, as we mentioned before, we should not use .equals() for comparisons. The .equals() method of the BigDecimal class is only used to compare if the two pointers point to the same object (in this case, the string representation of the number in the string pool). Consider the following example.
import java.math.BigDecimal //do not forget the import statement public class BigDecimalExample4 { public static void main(String[]args){ BigDecimal a = new BigDecimal("3"); BigDecimal b = new BigDecimal("3.00"); BigDecimal c = new BigDecimal("3"); System.out.println("a equals b ? = "+a.equals(b)); // false System.out.println("a equals c ? = "+a.equals(c)); // true } }
Output:
a equals b ? = false a equals c ? = true
Here, even though a was 3 and b was 3.00 (essentially having the same value), using .equals returns false, since these two objects consist of 2 different string literals. On the other hand, a (“3”) and c (“3”) match because they are the exact same string literal. Always use .compareTo for value comparison.
Summary
In this post, we discussed what is a BigDecimal and how to use it in Java to perform high accuracy calculations. We discussed some of the common applications that may require BigDecimals. Finally we discussed how to perform comparison operations on BigDecimals and the common pitfalls that you may encounter.
Leave a Reply
You must be logged in to post a comment.