__Java BigDecimal, Dealing with high precision calculations:__

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 calculation.

As we have seen in the primitive data types tutorial, a floating point 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. The problem is that in order to calculate the surface area of the object up to 20 decimal

places, we would have to use the value of

As we have seen in the primitive data types tutorial, a floating point 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. The problem is that 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. If we try to use a double data type in Java, the following would happen:
public class BigDecimalExample1 { |

public static void main(String[]args){ |

double pi = 3.846518214 |

System.out.println(pi); |

} |

} |

When we try to print the value of pi, the following value is printed.

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. A BigDecimal has the ability to store floating point numerics with a high degree

of accuracy. We will start by first defining

data type "double" has and this is where a BigDecimal object comes in. A BigDecimal has the ability to store floating point numerics with a high degree

of accuracy. We will start by first defining

*pi*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

Let us run the program and find how

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).

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

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:

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 number 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

the number of decimal places we want, and the rounding mode specifies how we would like to round a floating point number. As an example, we will

modify the task above as follows :

"After you have created the program, your bosses at NASA have changed their minds, and they now want the result accurate up to no more than 3 decimal

places. You are now required to change your program."

To do so, we will use the ".setScale(...)" method just before the printing. We will also use

the BigDecimal. In that case, we will have to specify two properties of a BigDecimal object. The

*rounding mode*and the*scale.*The scale definesthe number of decimal places we want, and the rounding mode specifies how we would like to round a floating point number. As an example, we will

modify the task above as follows :

"After you have created the program, your bosses at NASA have changed their minds, and they now want the result accurate up to no more than 3 decimal

places. You are now required to change your program."

To do so, we will use the ".setScale(...)" method just before the printing. We will also use

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 order to limit a number to a certain degree of precision, it is advisable to

set a rounding mode for BigDecimal to use. 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 received the following output:

set a rounding mode for BigDecimal to use. 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 received 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.

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 of they point to the same object, and has nothing to do with numeric equality). The compareTo method works as

follows:

lets say we make the call "x.compareTo(y)". The result of this call will be : (as in the comparable interface)

-1 : if x < y

0 : if x == y

+1: if x > y

To illustrate, consider the following code, and it's corresponding output:

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 of they point to the same object, and has nothing to do with numeric equality). The compareTo method works as

follows:

lets say we make the call "x.compareTo(y)". The result of this call will be : (as in the comparable interface)

-1 : if x < y

0 : if x == y

+1: if x > 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

Note: As we said before,

pointers point to the same object (in this case, the string representation of the number in the string pool). Consider the following example:

__DONT USE .equals() for comparisons__*The .equals() method in the case of BigDecimals 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 (3) and b (3.00) have 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.

On the other hand, a (3) and c (3) match because they are the exact same string literal. Always use .compareTo for value comparison.