Chitika

Showing posts with label BigDecimal. Show all posts
Showing posts with label BigDecimal. Show all posts

Monday, January 31, 2005

double vs. BigDecimal

Apart of continuous education and code reviews, there are still some Java codes I've found among my colleagues which are created without giving much thought on the floating-point arithmetic in Java. This is contradictory to the fact that a consistent floating-point arithmetic is essential for any financial applications.

Try and run the following code snippet:


public static void main (String[] args) {
    System.out.println (
        "(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1) = " +
        (0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1));

    double d = 0.0;
    while (d <= 1.0) d += 0.1;
    System.out.println ("d = " + d);

    System.out.println ("0.0175 * 100000 = " + 0.0175 * 100000);
}

and guess, what's the output?
You might've guessed that 1.0, 1.0, 1750.0 are the outputs, right?
Well, the results are..

(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1) = 0.9999999999999999
d = 1.0999999999999999
0.0175 * 100000 = 1750.0000000000002

So, what's the conclusion here?
1. Do not use double/float for floating-point arithmetic in Java, use BigDecimal instead. This is because Java cannot represent floating-point precisely.
2. Do not use == or != as a floating-point comparison. Compare Float.floatToIntBits (float) or Double.doubleToLongBits (double) instead. If == or != is used on float/double, there's a possibility that the code will go into infinite loop.
3. Always use BigDecimal for temporary variables, which will be processed/involved in future calculations. Convert the values to float/double only if you want to persist them into the database.

Here's an example of the code to add using BigDecimal:

// default to read a double primitive value of 18 digit
// precision
public static final NumberFormat DEFAULT_DECIMAL_FORMAT =
    new DecimalFormat ("#.0#################");
public static final BigDecimal ZERO = new BigDecimal ("0");

public static BigDecimal add (double a, double b) {
    String s = DEFAULT_DECIMAL_FORMAT.format(a);
    BigDecimal bd = new BigDecimal (s);
    return add (bd, b);
}

public static BigDecimal add (BigDecimal a, double b) {
    String s = DEFAULT_DECIMAL_FORMAT.format(b);
    BigDecimal bd = new BigDecimal (s);
    return add (a, bd);
}

public static BigDecimal add (BigDecimal a, BigDecimal b) {
    if (a == null) return (b == null) ? ZERO : b;
    return a.add (b);
}

Applying the code above, we'll have the following code:

System.out.println (
    "add (add (add (add (add (add (add (add (add (0.1, 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1) = " +
    add (add (add (add (add (add (add (add (add (0.1, 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1));
System.out.println (
    "new BigDecimal (\"0.0175\").multiply (new BigDecimal (\"100000\").doubleValue()) = " +
    new BigDecimal ("0.0175").multiply (new BigDecimal ("100000")).doubleValue());

And the results are..

add (add (add (add (add (add (add (add (add (0.1, 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1), 0.1) = 1.0
new BigDecimal ("0.0175").multiply (new BigDecimal ("100000").doubleValue()) = 1750.0

A note about BigDecimal is don't use the double constructor, instead use the String constructor. There is no point in trying to do the right thing if you're giving it the bad/wrong seed.. :D

Further Reading:
Effective Java Programming Language Guide