How to support currency

Many applications deal with currency and it is frequently an integral component of business applications. There are many issues that complicate the representation and use of currency values such as precision and accuracy. In addition, there are differences between how currency types such as rubles and the yen are displayed. In this recipe, we will examine the data types we can use to represent currency and locale-specific issues regarding currency.

Getting ready

There are several potential data types that can be used for representing currency including:

  • Floating point numbers
  • Integer
  • BigDecimal

To determine which is best we need to consider issues such as precision and accuracy. Ease of use is another concern since we will need to be able to manipulate currency values. We also need to consider the locale to insure the currency we are using is expressed in a locale-specific format.

Floating points are a poor choice for representing currency. Their major drawback is their lack of precision. To understand this limitation, consider how we represent the fraction 1/3 as a decimal number: 0.33333. We cannot precisely represent this fraction as a decimal number since it would take an infinite number of trailing 3s. The fraction 2/3 has a similar problem: 0.66666. Adding 1/3 plus 2/3 gives 1. However, adding 0.33333 plus 0.66666 gives 0.99999. Close, but it does not provide sufficient precision. While we can round the result, floating point numbers do not provide us with much control over the rounding process. Floating point numbers are not exact while currency values require exactness.

Integers could be used but it requires the developer to explicitly keep track of the decimal point. Assuming this is not too burdensome, which it probably is in operations like multiplication, we still need to display the results of our calculations. This will require us to extract the last two digits (assuming cents and dollars is our currency) and convert the result to a properly formatted string. This approach has not been widely used due to the lack of support.

The BigDecimal class is a member of the java.math package. It supports arbitrary precision decimal numbers and standard arithmetic operations. One drawback to its use is each BigDecimal object is immutable which can result in inefficiencies associated with the creation and garbage collection of objects. However, it is the preferred approach.

How to do it...

A BigDecimal number can be created using a number of overloaded constructors. One approach is to use a single double number as the argument of the constructor. Here, 1045.32 is used to create a BigDecimal object. The value is displayed without formatting and the scale method is used to determine the number of digits to the right of the decimal point.

BigDecimal amount;
amount = amount.add(new BigDecimal(1045.32));
System.out.println("amount Unformatted: " + amount.toString());
System.out.println("Scale of amount: " + amount.scale());

The console output appears as follows:

INFO: amount Unformatted: 1045.319999999999936335370875895023345947265625

INFO: Scale of amount: 42

Contrast this with creating a BigDecimal number using a string.

BigDecimal amount2;
amount2= new BigDecimal("1045.32");
System.out.println("amount2 Unformatted: " + amount2.toString());
System.out.println("Scale of amount: " + amount2.scale());

The output follows:

INFO: amount2 Unformatted: 1045.32

INFO: Scale of amount: 2

When formatting a BigDecimal, we can use the java.text.NumberFormat class. Its getCurrencyInstance method returns a NumberFormat object formatted according to a specific locale. Below, we use the Locale.US with the amount2 variable:

NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.US);
System.out.println("amount2 formatted: " + numberFormat.format(amount2));

The output will appear as follows:

INFO: amount2 formatted: $1,045.32

Basic arithmetic operations are supported by BigDecimal. For example, to add two numbers we can use the add method.

BigDecimal number1 = new BigDecimal("32.54");
BigDecimal number2 = new BigDecimal("8.44");
number1 = number1.add(number2);
System.out.println(numberFormat.format(number1));

This results in the following console output:

INFO: $40.98

BigDecimal supports other operations including subtraction, multiplication, division, and exponentiation.

How it works...

The first set of code examples illustrated the use of a string to construct a BigDecimal object. This approach simplified the setting of its scale. The scale is determined by the number contained in the string. A number represented as a string with three digits after the decimal point would have a scale of 3. Using a string is the preferred way of converting a floating point value to a BigDecimal. The use of a constructor using a float or double value can sometimes be unpredictable.

The third example illustrated the use of the NumberFormat class to format the appearance of a number. The getCurrencyInstance method used a Local value to format the value based on a particular local.

The fourth example demonstrated how to perform arithmetic operations on BigDecimal objects. This is further elaborated in the next section.

There's more...

The BigDecimal class is a useful tool for dealing with currency. However, we need to be careful in a few areas:

  • Understanding the implications of immutable BigDecimal objects
  • Comparison of BigDecimal objects
  • When to perform rounding

Understanding the implications of immutable BigDecimal objects

In the previous example, two numbers were added together and assigned to the first number.

BigDecimal number1 = new BigDecimal("32.54");
BigDecimal number2 = new BigDecimal("8.44");
number1 = number1.add(number2);

This is the correct way to add the numbers. However, do not use the following approach and expect number1 to be modified:

number1.add(number2);

Remember, BigDecimal objects are immutable. This operation modifies neither number1 nor number2 but returns a new BigDecimal object containing their sum.

A common requirement is to keep a cumulative sum. The following code illustrates the essential elements of this technique:

BigDecimal total = BigDecimal.ZERO;
for(...) {
total = total.add(numbers[i]);
}

Notice the use of BigDecimal.ZERO. This constant represents a zero and is used to initialize total. The value returned by numbers[i] is added with total and the residual value is assigned to total. There are two other constants available: BigDecimal.ONE and BigDecimal.TEN representing the values of 1 and 10 respectively.

Comparison of BigDecimal numbers

When comparing two BigDecimal numbers, we have to be careful when using the equals method. Normally, this is the preferred method for comparing two objects as the equality operator simply compares reference values.

The BigDecimal's equals method bases its comparison on the values of the number and the scale used. Consider the following example. Two BigDecimal numbers are assigned the same number except for the number of digits to the right of the decimal point. The equals method returns false. However, the compareTo method works properly.

number1 = new BigDecimal("1.00");
number2 = new BigDecimal("1.000");
System.out.println(number1.equals(number2));
System.out.println(number1.compareTo(number2)==0);
System.out.println();

The output:

INFO: false

INFO: true

The equals method uses the number and the scale to determine equality. Since the two numbers have a different scale, they are not considered to be equal. The compareTo method returns a negative value if number1 is less than number2, a zero if they are equal and a positive number if number1 is greater than number2.

When to perform rounding

Rounding can be an important part of a computation. BigDecimal provides several rounding options. When rounding should be applied is dependent upon the nature of the calculation. Consider the addition of 0.134 and 0.133. If we add the numbers together and then round to two decimal places we get 0.27. However, if we round the two numbers first to 0.13 then add them together their sum is 0.26. The right approach is dependent on the problem we are trying to solve.

Rounding is supported using the round method. This method takes a java.math.MathContext object. The two argument constructor used below takes the precision as its first argument and a rounding method as its second argument. The rounding options are similar to those used with BigDecimal as explained shortly.

In the following example, we implement the addition problem discussed previously. The variables, number1 and number2, are added together and then rounded. The numbers, number3 and number4 corresponding to the first two numbers, are rounded and then added. All rounding uses a precision of two digits.

number1 = new BigDecimal("0.0134");
number2 = new BigDecimal("0.0133");
BigDecimal number3 = number1.round(new MathContext(2,RoundingMode.HALF_UP));
BigDecimal number4 = number2.round(new MathContext(2,RoundingMode.HALF_UP));
System.out.println(number1.add(number2).round(new MathContext(2,RoundingMode.HALF_UP)));
System.out.println(number3.add(number4).round(new MathContext(2,RoundingMode.HALF_UP)));
System.out.println();

The output below confirms the differences in the addition using the two rounding approaches.

INFO: 0.027

INFO: 0.026

The BigDecimal class provides eight rounding techniques. If the rounding mode is not specified, it uses the most appropriate scale and rounding based on the operation performed. While not detailed here, the BigDecimal.ROUND_HALF_UP will round up if the fraction part is greater than or equal to 0.5. However, BigDecimal.ROUND_HALF_EVEN best minimizes errors that can accumulate over a series of calculations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.96.86