Echando código: ¿Porqué usar BigDecimal para cálculos financieros y no Double?



La razón es simple: Java no puede representar exactamente un número como un double, asi que si es necesario (por ejemplo para matemática financiera) es mejor utilizar la clase ‘Java.math.BigDecimal’.

Si bien la clase no es perfecta (es más dificil de usar que un simple número y los cálculos som más lentos que usando números regulares) si se necesita precisición entonces es la única manera de hacerlo en Java (hay trucos trabajando con matemática entera, pero de nuevo yo pienso que estas librerías ya fueron codificadas por expertos en el área y en el futuro mejorarán su desempeño).

El libro ‘Effective Java’ (del cual ya hablé en una oportunidad) presenta ejemplos concretos de porqué no hacerlo, y este tutorial de JavaWorld es bien completo además.

Como siempre, les dejo un pequeño pedazo de código para que jueguen con él. En este caso es el cálculo del valor presente neto (NPV) en una clase ultra sencilla de Java:


import java.math.BigDecimal;

/**
* This class shows how to use the BigDecimal class for numeric
calculation that require to be exact.
* This particular class implements the formula of present value. The
correct value was calculated using the
* OpenOffice function :NPV(rate, range1, range2, ...). Only showing
the first 20 decimals.
* @author Jose Vicente Nunez Zuleta (josevnz@yahoo.com)
* @version 0.1
* @see http://www.investopedia.com/articles/03/101503.asp (check the
example here. Also the calculator shows the wrong values: 209.21, not
133.74)
* @see http://www.developer.com/java/other/article.php/631281
* @see http://www.developer.com/java/article.php/788311
*/
public final class PV {

private static BigDecimal power(BigDecimal number, int power) {
BigDecimal powerc = number;
for (int j=1; j < power; j++) {
powerc = powerc.multiply(number);
}
return powerc;
}
public static double regularNPV(double [] cashflows, double
discountRate) {
double pv = cashflows[0];
for (int i=1; i < cashflows.length; i++) {
pv += cashflows[i] / Math.pow((1 + discountRate), i);
}
return pv;
}

public static BigDecimal correctNPV(BigDecimal [] cashflows,
BigDecimal discountRate) {
BigDecimal pv = cashflows[0];
for (int i=1; i < cashflows.length; i++) {
pv = pv.add(cashflows[i].divide(power(discountRate.add(new
BigDecimal("1.0")), i), BigDecimal.ROUND_HALF_EVEN));
}
return pv;
}

public static void main(String [] args) {

// Calculation of the present value, "traditional" way
double [] cashflows = { -1000.0f, 500.0f, 400.0f, 300.0f, 200.0f,
100.0f };
double discountRate = 10.0f / 100.0f;
double pv = regularNPV(cashflows, discountRate);

BigDecimal [] cashflows2 = new BigDecimal [6];
BigDecimal discountRate2 = new BigDecimal("0.1");
cashflows2[0] = new BigDecimal("-1000.0");
cashflows2[1] = new BigDecimal("500.0");
cashflows2[2] = new BigDecimal("400.0");
cashflows2[3] = new BigDecimal("300.0");
cashflows2[4] = new BigDecimal("200.0");
cashflows2[5] = new BigDecimal("100.0");
BigDecimal pv2 = correctNPV(cashflows2, discountRate2);

System.out.println("PV Theoric: $133.74645298694200000000");
System.out.println("PV Regular: $" + pv);
System.out.println("PV Proper: $" + pv2.toString());
}
}