Make cents with BigDecimal

Write Java programs to calculate and format currency

To be able to write Java programs that deal with finances, you need to know about the

BigDecimal

class and numeric formatting. The first step to teaching you both of those topics is to first create

BigDecimal

objects. We will use the

BigDecimal

class in the

java.math

library to hold values. You can create a

BigDecimal

object in the following manner:

BigDecimal amount = new BigDecimal("1115.37");

In the above case, the String argument to the BigDecimal constructor determines the value of the object created. The value of "1115.37" might represent, for example, a monthly mortgage payment in dollars, or a checkbook balance. To display the amount, you can use the BigDecimal class’s toString() method:

     System.out.println(amount.toString());

A program that creates and displays a BigDecimal amount is shown below:

import java.math.*;
public class Mortgage {
   public static void main(String[] args) {
      BigDecimal payment = new BigDecimal("1115.37");
      System.out.println(payment.toString());
   }
}

Output from the above program is:

1115.37

Formatting currency

Since we are dealing with money, it would be nice to have the amounts held by BigDecimal objects properly formatted, which for US currency would include a dollar sign and a comma as a thousands separator. (For other currencies, please see the section Currency of Other Countries below). The NumberFormat class, found in the java.text library, can create an appropriate object for US currency with the following code:

     NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US);  

Note that the Locale class, used as an argument for the getCurrencyInstance() method above, is found in the java.util library.

The NumberFormat‘s format() method, which we will be using next, takes a double or long primitive as an argument, so first we will turn the BigDecimal object into a double using BigDecimal‘s doubleValue() method:

double doublePayment = payment.doubleValue(); 

Now we use NumberFormat‘s format() method to create a String:

 String s = n.format(doublePayment); 

Putting these steps in a program, we then have:

import java.math.*;
import java.text.*;
import java.util.*;
public class Mortgage2 {
   public static void main(String[] args) {
      BigDecimal payment = new BigDecimal("1115.37");
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double doublePayment = payment.doubleValue();
      String s = n.format(doublePayment);
      System.out.println(s);
   }
}

Output from the above program is:

,115.37

Readers should note that creating a double value involves a small loss in the value’s accuracy. While the inaccuracies are too small to be seen in this article’s examples, they are visible in very large amounts. Therefore, you cannot rely upon NumericFormat to produce accurate results with very large numbers (about 13 or more digits).

Currencies of other countries

In the previous example, we used Locale.US as the argument passed to the getCurrencyInstance() method to specify the currency of the country (United States) with which we’d be working. Java is not limited to working with US currency however. For example, you would use Locale.GERMANY, Locale.FRANCE, or Locale.ITALY to specify the currencies of Germany, France, and Italy, respectively. The topic of internationalization is a subject in its own right; see the Resources section for a link to more information.

BigDecimal operations

BigDecimal methods for adding and subtracting numbers are add() and subtract(), respectively. For example, to add 1,115.37 and 115.37, we could do the following:

BigDecimal balance = new BigDecimal("1115.37");
BigDecimal transaction = new BigDecimal("115.37");
BigDecimal newBalance = balance.add(transaction);  

The BigDecimal‘s newBalance object now holds the value of 1,230.74. Similarly, to subtract 115.37 from 1,115.37, we could use this code:

BigDecimal balance = new BigDecimal("1115.37");
BigDecimal transaction = new BigDecimal("115.37");
BigDecimal newBalance2 = balance.subtract(transaction);      

The BigDecimal‘s newBalance2 object now holds the value of 1,000.00. (Naturally, if we are talking about checkbook balances in real life, the subtract() method will be used much more often than the add() method, and the total amount subtracted from the checkbook balance will exceed the total amount added, or so it often seems.) You can accomplish multiplying and dividing with BigDecimal‘s multiply() and divide() methods. Multiplying is demonstrated in the following program:

import java.math.*;
import java.text.*;
import java.util.*;
public class Multiply {
   public static void main(String[] args) {
      BigDecimal d = new BigDecimal("1115.32");
      BigDecimal taxRate = new BigDecimal("0.0049");
      BigDecimal d2 = d.multiply(taxRate);
      System.out.println("Unformatted: " + d2.toString()); 
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double money = d2.doubleValue();
      String s = n.format(money);
      System.out.println("Formatted:  " + s);
   }
}

The output for the above code is shown below:

Unformatted:   5.465068
Formatted:      .46

Note the extra decimal places in the unformatted BigDecimal object as compared to the formatted output. In addition, formatting the value of the BigDecimal object causes the fraction — greater than one half — to be dropped. To manage the extra decimal places and the lack of rounding, we can use BigDecimal‘s setScale() method to set the number of decimal places. When using setScale(), we need to specify not only the number of decimal places, but how the number will be rounded, if rounding is necessary. The most common way of rounding — round up fractions half or greater, and round down all other fractions — can be specified with BigDecimal‘s constant ROUND_HALF_UP. Therefore, to set the number of decimal places to two and specify that fractions half and greater will be rounded up, we can write:

d2 = d2.setScale(2, BigDecimal.ROUND_HALF_UP);

Modifying the above program to add setScale(), we now have:

import java.math.*;
import java.text.*;
import java.util.*;
public class Multiply2 {
   public static void main(String[] args) {
      BigDecimal d = new BigDecimal("1115.32");
      BigDecimal taxRate = new BigDecimal("0.0049");
      BigDecimal d2 = d.multiply(taxRate);
      d2 = d2.setScale(2, BigDecimal.ROUND_HALF_UP);
      System.out.println("Unformatted: " + d2.toString()); 
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double money = d2.doubleValue();
      String s = n.format(money);
      System.out.println("Formatted:  " + s);
   }
}

Now the output is:

Unformatted:   5.47
Formatted:      .47

Now the BigDecimal value is rounded to two digits, rounding the value up, and the formatted String correctly displays the rounded value. Other constants useful in rounding are ROUND_HALF_DOWN and ROUND_HALF_EVEN. The first, ROUND_HALF_DOWN, rounds fractions of half and under down, and all others up. The second, ROUND_HALF_EVEN, rounds half fractions to the even number (e.g., 2.5 rounds to 2, while 3.5 rounds to 4), and fractions greater or less than half to the closest integer. When dividing BigDecimal objects, we are required to specify how the result will be rounded. For this article, we will round halves up. The following program shows some sample division:

import java.math.*;
import java.text.*;
import java.util.*;
public class Divide {
   public static void main(String[] args) {
      BigDecimal d = new BigDecimal("1115.32");
      BigDecimal days = new BigDecimal("30");
      BigDecimal d2 = d.divide(days, 2, BigDecimal.ROUND_HALF_UP); 
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double money = d2.doubleValue();
      String s = n.format(money);
      System.out.println(s);
   }
}

Output from the above program is:

7.18

Calculating interest

For this example, assume that a sum of ,500 will receive interest payments at an annual rate of 6.7 percent. Payments will be calculated quarterly, and we will calculate the first quarterly payment. To do so, we will use the formula I=PRT, where I is the amount of interest, P is the principal (9,500), R is the rate (6.7 percent annually), and T is the time (0.25 years). The program is:

import java.math.*;
import java.text.*;
import java.util.*;
public class Interest {
   public static void main(String[] args) {
      BigDecimal principal = new BigDecimal("9500.00");
      BigDecimal rate = new BigDecimal("0.067");
      BigDecimal time = new BigDecimal("0.25");
      BigDecimal temp = principal.multiply(rate);
      BigDecimal interest = temp.multiply(time);
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double money = interest.doubleValue();
      String s = n.format(money);
      System.out.println("First quarter interest:  " + s);    }
} 

Output from the above program is:

First quarter interest: 59.12

Mutual fund transactions

In this example, an investor owns 754.495 shares in a mutual fund. The investor makes an additional 00.00 purchase of shares at 0.38 per share. We will use the following Java program to answer two questions: How many shares does the investor own after purchase, and what is the current market value of the account after the purchase? We will assume that the mutual fund keeps track of share numbers to three decimal places:

import java.math.*;
import java.text.*;
import java.util.*;
public class Mutual {
   public static void main(String[] args) {
      BigDecimal shares = new BigDecimal("754.495");
      BigDecimal purchaseAmount = new BigDecimal("200.00"); 
      BigDecimal pricePerShare = new BigDecimal("10.38");
      BigDecimal sharesPurchased = purchaseAmount.divide(pricePerShare, 3, BigDecimal.ROUND_HALF_UP);
      shares = shares.add(sharesPurchased);
      BigDecimal accountValue = shares.multiply(pricePerShare); 
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double dAccountValue = accountValue.doubleValue();
      String sAccountValue = n.format(dAccountValue);
      System.out.println("Number of shares = " + shares.toString()); 
      System.out.println("Account value = " + sAccountValue);   }
}

The above program outputs:

Number of shares = 773.763
Account value = ,031.66

More formatting

In the above example, the number of shares happens to be less than 1,000. If it had been greater than 1,000, the program would have outputted the number without a comma to separate the thousands place from the other digits. We can create a NumberFormat object to format numbers in the US style (commas separate thousands, periods separate decimals) by using:

NumberFormat n2 = NumberFormat.getInstance(Locale.US);

Modifying the previous program to increase the number of shares to more than 1,000 and to display the number of shares formatted as we desire, we have:

import java.math.*;
import java.text.*;
import java.util.*;
public class Mutual2 {
   public static void main(String[] args) {
      BigDecimal shares = new BigDecimal("1754.495");
      BigDecimal purchaseAmount = new BigDecimal("2000.00"); 
      BigDecimal pricePerShare = new BigDecimal("10.38");
      BigDecimal sharesPurchased = purchaseAmount.divide(pricePerShare, 3, BigDecimal.ROUND_HALF_UP);
      shares = shares.add(sharesPurchased);
      BigDecimal accountValue = shares.multiply(pricePerShare); 
      NumberFormat n = NumberFormat.getCurrencyInstance(Locale.US); 
      double dAccountValue = accountValue.doubleValue();
      String sAccountValue = n.format(dAccountValue);
      NumberFormat n2 = NumberFormat.getInstance(Locale.US); 
      double dShares = shares.doubleValue();
      String sShares = n2.format(dShares);
      System.out.println("Number of shares = " + sShares); 
      System.out.println("Account value = " + sAccountValue);   }
}

The modified version (directly above) now outputs:

Number of shares = 1,947.173
Account value = 0,211.66

Caveats

If you are writing a program that you or others will depend on for financial calculations, it makes sense to be as careful as possible. First, test your program thoroughly. Second, consult professionals, such as accountants, if you have any doubt as to the correct formula, rounding rule, or any other monetary aspect.

Summing up

BigDecimal objects, which represent values, can be added, subtracted, multiplied, and divided. While you can display BigDecimal objects using the toString() method, it is often preferable to create a NumberFormat object to format doubles obtained from BigDecimal. Now you can add the value of making simple interest calculations and mutual fund transactions in your Java programs.

Robert Nielsen is a Sun Certified Java 2
Programmer. He holds a master’s degree in education, specializing
in computer-assisted instruction, and has taught in the computer
field for several years. He has also published computer-related
articles in a variety of magazines.

Source: www.infoworld.com