0

I am working on a client's site, and I'm writing an amortization schedule calculator in in ruby on rails. For longer loan term calculations, it doesn't seem to be breaking when the balance reaches 0

Here is my code:

def calculate_amortization_results
    p = params[:price].to_i
    i = params[:rate].to_d
    l = params[:term].to_i
    j = i/(12*100)
    n = l * 12
    m = p * (j / (1 - (1 + j) ** (-1 * n)))
    @loanAmount = p
    @rateAmount = i
    @monthlyAmount = m
    @amort = []
    @interestAmount = 0
    while p > 0
        line = Hash.new
        h = p*j
        c = m-h
        p = p-c
        line["interest"] = h
        line["principal"] = c
        if p <= 0
            line["balance"] = 0
        else
            line["balance"] = p 
        end
        line["payment"] = h+c
        @amort.push(line)
        @interestAmount += h
    end
end

And here is the view:

- @amort.each_with_index do |a, i|
    %li
        .m
            = i+1
        .i
            = number_to_currency(a["interest"], :unit => "$")
        .p
            = number_to_currency(a["principal"], :unit => "$")
        .pp
            = number_to_currency(a["payment"], :unit => "$")
        .b
            = number_to_currency(a["balance"], :unit => "$")

What I am seeing is, in place of $0.00 in the final payment balance, it shows "-$-inf", iterates one more loop, then displays $0.00, but shows "-$-inf" for interest. It should loop until p gets to 0, then stop and set the balance as 0, but it isn't. Any idea what I've done wrong?

The calculator is here. It seems to work fine for shorter terms, like 5 years, but longer terms cause the above error.

Edit:

Changing the while loop to n.times do

and then changing the balance view to

= number_to_currency(a["balance"], :unit => "$", :negative_format => "$0.00")

Is a workaround, but i'd like to know why the while loop wouldn't work correctly

2 Answers 2

3

in Ruby the default for numerical values is Fixnum ... e.g.:

> 15 / 4
 => 3 

You will see weird rounding errors if you try to use Fixnum values and divide them.

To make sure that you use Floats, at least one of the numbers in the calculation needs to be a Float

> 15.0 / 4
 => 3.75 
> 15 / 4.0
 => 3.75 

You do two comparisons against 0 , which should be OK if you make sure that p is a Float.

As the other answer suggests, you should use "decimal" type in your database to represent currency.

Please try if this will work:

def calculate_amortization_results
    p = params[:price].to_f     # instead of to_i
    i = params[:rate].to_f     # <-- what is to_d ?   use to_f
    l = params[:term].to_i
    j = i/(12*100.0)            # instead of 100
    n = l * 12
    m = p * (j / (1 - (1 + j) ** (-1 * n)))  # division by zero if i==0 ==> j==0
    @loanAmount = p
    @rateAmount = i
    @monthlyAmount = m
    @amort = []
    @interestAmount = 0.0        # instead of 0
    while p > 0
        line = Hash.new
        h = p*j
        c = m-h
        p = p-c
        line["interest"] = h
        line["principal"] = c
        if p <= 0
            line["balance"] = 0
        else
            line["balance"] = p 
        end
        line["payment"] = h+c
        @amort.push(line)
        @interestAmount += h
    end
end

If you see "inf" in your output, you are doing a division by zero somewhere.. better check the logic of your calculation, and guard against division by zero.


according to Wikipedia the formula is: http://en.wikipedia.org/wiki/Amortization_calculator

to improve rounding errors, it's probably better to re-structure the formula like this:

 m = (p * j) / (1 - (1 + j) ** (-1 * n)    # these are two divisions! x**-1 == 1/x

which is equal to:

 m = (p * j) + (p * j) / ((1 + j) ** n) - 1.0)

which is equal to: (use this one)

 q = p * j   # this is much larger than 1 , so fewer rounding errors when dividing it by something
 m = q + q / ((1 + j) ** n) - 1.0)   # only one division
Sign up to request clarification or add additional context in comments.

Comments

2

I think it has something to do with the floating point operations precision. It has already been discussed here: Ruby number precision with simple arithmetic and it would be better to use decimal format for financial purposes.

The answer could be computing the numbers in the loop, but with precomputed number of iterations and from the scratch.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.