Which data type would you choose for storing Monetary Values, Currency in Java

Upasana | August 10, 2019 | 5 min read | 1 views


Float & Double are bad for financial (even for military use) world, never use them for monetary calculations. If precision is one of your requirements, use BigDecimal instead.

Lets see the problem with the help of this example:

All floating point values that can represent a currency amount (in dollars and cents) can not be stored exactly as it is in the memory. So if we want to store 0.1 dollar (10 cents), float/double can not store it as it is, instead binary can store only a closer approximation value (0.100000001490116119384765625 in decimal). The magnitude of this problem becomes significant (known as loss of significance) when we repetitively do arithmetic operations (multiply or divide) using these two data types. Let’s try to understand this fact by taking this simple example

Loss of precision using double
public class DoubleForCurrency {

    public static void main(String[] args) {
        double total = 0.2;
        for (int i = 0; i < 100; i++) {
            total += 0.2;
        }
        System.out.println("total = " + total);
    }
}
Program output:
total = 20.19999999999996

The output should have been 20.20 (20 dollars and 20 cents), but floating point calculation made it 20.19999999999996. That’s loss of precision (or loss of significance)

Real cause of loss of significance

Floating-point arithmetic

In computing, floating-point arithmetic (FP) is arithmetic using formulaic representation of real numbers as an approximation so as to support a trade-off between range and precision.

From Wikipedia

Whether or not a rational number has a terminating expansion depends on the base. For example, in base-10 the number 1/2 has a terminating expansion (0.5) while the number 1/3 does not (0.333…​). In base-2 only rationals with denominators that are powers of 2 (such as 1/2 or 3/16) are terminating. Any rational with a denominator that has a prime factor other than 2 will have an infinite binary expansion. This means that numbers which appear to be short and exact when written in decimal format may need to be approximated when converted to binary floating-point. For example, the decimal number 0.1 is not representable in binary floating-point of any finite precision; the exact binary representation would have a "1100" sequence continuing endlessly:

e = −4; s = 1100110011001100110011001100110011…​, where, as previously, s is the significand and e is the exponent.

When rounded to 24 bits this becomes

e = −4; s = 110011001100110011001101, which is actually 0.100000001490116119384765625 in decimal.

BigDecimal for the rescue

BigDecimal represents a signed decimal number of arbitrary precision with an associated scale. BigDecimal provides full control over the precision and rounding of the number value. Virtually its possible to calculate value of pi to 2 billion decimal places using BigDecimal, available physical memory being the only limit.

That’s the reason we should always prefer BigDecimal or BigInteger for financial calculations.

Special Notes

Primitive type - int and long are also useful for monetary calculations if decimal precision is not required.

We should really avoid using BigDecimal(double value) constructor instead prefer BigDecimal(String) because BigDecimal (0.1) results in (0.1000000000000000055511151231257827021181583404541015625) being stored in BigDecimal instance. In contrast BigDecimal ("0.1") stores exactly 0.1

Question: What is Precision and Scale?

Precision is the total number of digits (or significant digits) of a real number

Scale specifies number of digits after decimal place For example, 12.345 has precision of 5 (total digits) and scale of 3 (number of digits right of the decimal)

How to format BigDecimal Value without getting exponentiation in the result & Strip the trailing zeros?

We might get exponentiation in the calculation result if we do not follow some best practices while using BigDecimal. Below is the code snippet which shows a good usage example of handling the calculation result using BigDecimal.

BigDecimal Rounding
import java.math.BigDecimal;

public class BigDecimalForCurrency {

    public static void main(String[] args) {
        int scale = 4;
        double value = 0.11111;
        BigDecimal tempBig = new BigDecimal(Double.toString(value));
        tempBig = tempBig.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
        String strValue = tempBig.stripTrailingZeros().toPlainString();
        System.out.println("tempBig = " + strValue);
    }
}
Program Output
tempBig = 0.1111
How would you print a given currency value for Indian Locale (INR Currency)?

NumberFormat class is designed specifically for this purpose. Currency symbol & Rounding Mode is automatically set based on the locale using NumberFormat. Lets see this in action

NumberFormat example
class Scratch {
    public static String formatRupees(double value) {
        NumberFormat format = NumberFormat.getCurrencyInstance(new Locale("en", "in"));
        format.setMinimumFractionDigits(2);
        format.setMaximumFractionDigits(5);
        format.setRoundingMode(RoundingMode.HALF_EVEN);
        return format.format(value);
    }

    public static void main(String[] args) {
        BigDecimal tempBig = new BigDecimal(22.121455);
        System.out.println("tempBig = " + formatRupees(tempBig.doubleValue()));
    }
}
Program Output
tempBig = Rs.22.12146

That’s all, everything is taken care by NumberFormat.

Some precautions

  • BigDecimal(String) constructor should always be preferred over BigDecimal(Double), because using BigDecimal(double) is unpredictable due to inability of the double to represent 0.1 as exact 0.1

  • If double must be used for initializing a BigDecimal, use BigDecimal.valueOf(double) which converts Double value to string using Double.toString(double) method

  • Rounding mode should be provided while setting the scale

  • StripTrailingZeros chops off all the trailing zeros

  • toString() may use scientific notation but, toPlainString() will never return exponentiation in its result

Do you know?

Use of float and double instead of BigDecimal could be fatal in military world

On February 25, 1991, a loss of significance in a MIM-104 Patriot missile battery prevented it from intercepting an incoming Scud missile in Dhahran, Saudi Arabia, contributing to the death of 28 soldiers from the U.S. Army’s 14th Quartermaster Detachment - http://www.gao.gov/products/IMTEC-92-26

Banker’s Rounding Mode

Since the introduction of IEEE 754, the default method (round to nearest, ties to even, sometimes called Banker’s Rounding or RoundingMode.HALF_EVEN) is more commonly used USA. This method rounds the ideal (infinitely precise) result of an arithmetic operation to the nearest representable value, and gives that representation as the result. In the case of a tie, the value that would make the significand end in an even digit is chosen.

For further reading -


Buy my ebook for complete question bank

Most of these questions has been answered in my eBook "Cracking the Core Java Interview" updated on June 2018, that you can buy from this link:

Buy from Shunya (DRM Free PDF download with updates)

Top articles in this category:
  1. Multi-threading Java Interview Questions for Investment Bank
  2. Cracking core java interviews - question bank
  3. ION Trading Java Interview Questions
  4. Hibernate & Spring Data JPA interview questions
  5. Difference between getOne and findById in Spring Data JPA?
  6. Goldman Sachs Java Interview Questions
  7. Java Concurrency Interview Questions

Recommended books for interview preparation:

Find more on this topic: