Search code examples
javaandroidmathfloating-pointieee

Math.pow strange results


I'm working on a mortgage calculation equation and I'm getting differing results from Math.pow() and I don't know why.

Here is the test setup:

double interestRatePercent = 7;
double monthlyInterestRate = (interestRatePercent / 100) / MONTHS_PER_YEAR;
int numberOfPayments = loanTermInYears * MONTHS_PER_YEAR;

Log.i(TAG, String.format("monthlyInterestRate: %f", monthlyInterestRate));
Log.i(TAG, String.format("numberOfPayments: %d", numberOfPayments));
Log.i(TAG, "  ");

Log.i(TAG, "Hardcoded result:");
double hardcodedResult = Math.pow(1.0 + 0.005833, numberOfPayments);
Log.i(TAG, String.format("(1 + 0.005833)^360 = %f", hardcodedResult));
Log.i(TAG, "  ");

Log.i(TAG, "Parameterized result:");
double paramResult = Math.pow(1.0 + monthlyInterestRate, numberOfPayments);
Log.i(TAG, String.format("(1 + %f)^%d = %f", monthlyInterestRate, numberOfPayments, paramResult));
Log.i(TAG, "  ");

Log.i(TAG, "BigDecimal result:");
BigDecimal bigResult = BigDecimal.valueOf(1.0 + monthlyInterestRate);
bigResult = bigResult.pow(numberOfPayments);
Log.i(TAG, String.format("(1 + %f)^%d = %f", monthlyInterestRate, numberOfPayments, bigResult));
Log.i(TAG, "  ");
Log.i(TAG, "  ");

Here is the test results:

monthlyInterestRate: 0.005833
numberOfPayments: 360

Hardcoded result:
(1 + 0.005833)^360 = 8.115529

Parameterized result:
(1 + 0.005833)^360 = 8.116497

BigDecimal result:
(1 + 0.005833)^360 = 8.116497

Only the hard-coded result is correct. Both the Math.pow and BigDecimal.pow results are bad.

Any ideas?


Solution

  • BigDecimal bigResult = BigDecimal.valueOf(1.0 + monthlyInterestRate);

    When you are creating your BigDecimal, you'll get loss of precision, since 1.0 + monthlyInterestRate will lose precision. To get better precision, create a BigDecimal for 1.0, then use the add() method to add it to another BigDecimal with the value 0.005833. Then use the BigDecimal pow() function.

    In other words, construct the BigDecimal from simpler BigDecimals not int or double types.