BigDecimal Class in Java
1. Introduction
Are you planning to write a Java program that requires total control over the scaling of decimal numbers? BigDecimal is the class you need to deal with. In this tutorial, you will learn what a BigDecimal is. You'll also learn some operations you can perform on BigDecimals and common use cases where you should go for BigDecimal instead of the traditional Double class.
2. How to Create a BigDecimal
According to its Javadoc, a BigDecimal represents an immutable, arbitrary-precision decimal number. A BigDecimal consists of an arbitrary-precision integer unscaled value and a 32-bit integer scale.
There are two main ways of creating a BigDecimal: using constructors and using static methods BigDecimal#valueOf()
.
2.1. BigDecimal Constructors
BigDecimal offers multiple constructors with different parameter types: BigInteger, long, double, char, String, etc. Some of the constructors also accept a MathContext
object which provides a way to specify the scale and the rounding mode. In this section, we will provide some of the most used constructors.
2.1.1. BigDecimal(double val)
This code will construct a BigDecimal object from the double value 1.5.
BigDecimal bigDecimal = new BigDecimal(1.5);
System.out.println(bigDecimal);
Output:
1.5
However, this way of creating a BigDecimal might produce unexpected output because the value passed to the constructor needs to be converted to a floating-point first. This conversion to a floating-point number may lead to a precision issue.
To understand this issue, try to execute the following code:
BigDecimal bigDecimal = new BigDecimal(0.1);
System.out.println(bigDecimal);
Output:
0.1000000000000000055511151231257827021181583404541015625
As you can see, the output is not what you could have expected (0.1). This is because the value "0.1" cannot be translated into an exact floating-point number. Instead, an approximated value is returned.
For this reason, it is recommended to use the
String
constructor because it does not require an intermediate conversion to a floating-point number.
2.1.2. BigDecimal(String val)
This is the recommended way of creating a BigDecimal, especially if the source value is a floating-point number (double or float).
BigDecimal bigDecimal = new BigDecimal("0.1");
System.out.println(bigDecimal);
Output:
0.1
As you can see, with this constructor you obtain the exact representation of the String
value.
2.1.3. BigDecimal(String val, MathContext mc)
Optionally, we may need the value to be with a specific precision. In that case, you must provide a MathContext
object along with the String
parameter.
In the following example, we create a MathContext
object with a precision of 3 significant digits.
MathContext mc = new MathContext(3);
BigDecimal bigDecimal = new BigDecimal("0.1", mc);
System.out.println(bigDecimal);
Output:
0.10
As a result, the displayed value has 2 digits after the decimal symbol.
2.2. BigDecimal#valueOf()
Java also provides static factory methods to create a BigDecimal from a BigInteger, a double, or a long. You should prefer these methods when converting primitive types (long, double) or BigIntegers to BigDecimals. The reason is that these methods ensure that the conversion maintains precision and prevents you from the pitfalls due to floating-point arithmetic.
2.2.1. BigDecimal#valueOf(double)
BigDecimal value = BigDecimal.valueOf(0.1);
System.out.println(value);
Output:
0.1
2.2.2. BigDecimal#valueOf(long)
BigDecimal value = BigDecimal.valueOf(10L);
System.out.println(value);
Output:
10
3. Arithmetic Operations
You can perform the usual arithmetic operations on BigDecimals, including addition, subtraction, multiplication, and division. Since BigDecimal is an immutable class, all these operations return the result in a new object.
3.1. Addition
The following code will return a new BigDecimal whose value is myValue + toAdd
.
BigDecimal myValue = new BigDecimal("1.5");
BigDecimal toAdd = new BigDecimal("0.50");
BigDecimal result = myValue.add(toAdd);
System.out.println(result);
Output:
2.00
If no scale is provided, the preferred scale is the maximum of
myValue.scale() and toAdd.scale()
.
If you don't want the behavior of defaulting the scale, you can provide a MathContext
object to the add()
method:
MathContext mc = new MathContext(2, RoundingMode.HALF_UP);
BigDecimal myValue = new BigDecimal("1.5");
BigDecimal toAdd = new BigDecimal("0.50");
BigDecimal result = myValue.add(toAdd,mc);
System.out.println(result);
Output:
2.0
3.2. Subtraction
Just like the addition, BigDecimal provides two methods to perform subtraction operations.
Without a MathContext
BigDecimal myValue = new BigDecimal("1.5");
BigDecimal toSubtract = new BigDecimal("0.50");
BigDecimal result = myValue.subtract(toSubtract);
System.out.println(result);
Output:
1.00
Just like the addition, if no scale is provided, the resulted scale is the maximum of
myValue.scale()
andtoSubstract.scale()
.
With a MathContext
MathContext mc = new MathContext(2, RoundingMode.HALF_UP);
BigDecimal myValue = new BigDecimal("1.5");
BigDecimal toSubtract = new BigDecimal("0.50");
BigDecimal result = myValue.subtract(toSubtract,mc);
System.out.println(result);
Output:
1.0
3.3. Multiplication
In the same way, as for addition and subtraction, multiplying two BigDecimals can be achieved in two ways: with a MathContext object and without. However, unlike addition and subtraction, if no scale is provided, the resulting scale is the sum of myValue.scale()
and toMultiply.scale()
.
Without a MathContext
BigDecimal myValue = new BigDecimal("1.5");
BigDecimal toMultiply = new BigDecimal("0.50");
BigDecimal result = myValue.multiply(toMultiply);//Summing the scale : 1 + 2 = 3
System.out.println(result);
Output:
0.750
With a MathContext
MathContext mc = new MathContext(2, RoundingMode.HALF_UP);
BigDecimal myValue = new BigDecimal("1.5");
BigDecimal toMultiply = new BigDecimal("0.50");
BigDecimal result = myValue.multiply(toMultiply,mc);
System.out.println(result);
Output:
0.75
3.4. Division
Similarly to the previous operators, Java provides different methods to perform BigDecimals division. However, in addition to providing a scale, you should also specify a rounding mode. Even though the RoundingMode
is not required, it's a good practice to provide it.
Just like the previous operators, Java will automatically compute the preferred scale if none is provided. The preferred scale is the difference: myValue.scale() - divisor.scale()
.
Without a MathContext
BigDecimal myValue = new BigDecimal("1.500");
BigDecimal divisor = new BigDecimal("0.5");
BigDecimal result = myValue.divide(divisor);//Preferred scale = 3 - 1 = 2
System.out.println(result);
Output:
3.00
With a MathContext
MathContext mc = new MathContext(2, RoundingMode.HALF_UP);
BigDecimal myValue = new BigDecimal("1.500");
BigDecimal divisor = new BigDecimal("0.5");
BigDecimal result = myValue.divide(divisor,mc);
System.out.println(result);
Output:
3.0
4. Comparing BigDecimals
BigDecimal#compareTo
4.1. Using BigDecimal implements the Comparable
interface and provides a compareTo()
method to compare two BigDecimal objects.
BigDecimal firstValue = new BigDecimal("1.5");
BigDecimal secondValue = new BigDecimal("2.5");
BigDecimal thirdValue = new BigDecimal("0.5");
BigDecimal fourthValue = new BigDecimal("1.50");
int result1 = firstValue.compareTo(secondValue);// - 1 meaning 1.5 < 2.5
int result2 = firstValue.compareTo(thirdValue);// 1 meaning 1.5 > 0.5
int result3 = firstValue.compareTo(fourthValue);// 0 meaning 1.5 = 1.50 regardless of the scale
BigDecimal#equals()
4.2. Using BigDecimal overrides the Object#equals()
method and provides a convenient way of comparing two BigDecimals for equality.
Two BigDecimals are considered equals if and only if they have the same value and the same scale.
BigDecimal firstValue = new BigDecimal("1.5");
BigDecimal secondValue = new BigDecimal("1.5");
boolean result = firstValue.equals(secondValue);//true
However:
BigDecimal firstValue = new BigDecimal("1.5");
BigDecimal secondValue = new BigDecimal("1.50");
boolean result = firstValue.equals(secondValue);//false : Same value but different scales
5. Common Use Cases for BigDecimal
For most programs manipulating decimal numbers, the Double
class is enough as it'll provide better performance. However, in some specific situations, BigDecimal is the recommended class. Here are common situations:
- High Precision Calculations: This includes financial applications where rounding errors can lead to significant inaccuracies.
- Scientific Calculations: In this situation, it's important to avoid issues due to floating-point arithmetic.
- Custom Rounding Requirements: In your application, you may need to apply specific rounding behavior (nearest integer, always up, always down, etc...)
To sum up, BigDecimal is recommended when precision is more important than performance.
6. Conclusion
In this post, you learned about the BigDecimal class in Java.
_This article was originally published at: https://nkamphoa.com