Which data type would you choose for storing currency values like Trading Price? What’s your opinion about Float, Double and BigDecimal?

Munish Chandel
4 min readAug 14, 2018

--

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.

https://en.wikipedia.org/wiki/Floating-point_arithmetic

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 could prove 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 -

For more such questions, you can get my ebook

Cracking Core Java Interviews v3.4 updated on April 2018

Buy from Shunya (DRM Free PDF download with updates)

Buy from Google Books

Buy from Pothi.com

--

--