Ruby / Rails while循环没有正确打破?

时间:2011-10-05 21:07:52

标签: ruby-on-rails

我正在客户的网站上工作,我正在ruby on rails上写一个摊销计划计算器。对于较长的贷款期限计算,当余额达到0

时,它似乎没有中断

这是我的代码:

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

以下是观点:

- @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 => "$")

我所看到的是,在最终付款余额中代替$ 0.00,它显示“ - $ - inf”,再循环一次,然后显示$ 0.00,但显示“ - $ - inf”的利息。它应该循环直到p变为0,然后停止并将余额设置为0,但事实并非如此。知道我做错了吗?

计算器为here。它似乎适用于较短的术语,如5年,但较长的术语会导致上述错误。

编辑:

将while循环更改为n.times do

然后将余额视图更改为

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

是一种解决方法,但我想知道为什么while循环无法正常工作

2 个答案:

答案 0 :(得分:3)

在Ruby中,数值的默认值是Fixnum ......例如:

> 15 / 4
 => 3 

如果您尝试使用Fixnum值并将它们分开,您将看到奇怪的舍入错误。

为确保使用Floats,计算中至少有一个数字需要为Float

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

你对0进行两次比较,如果你确定p是一个浮点数就应该没问题。

正如另一个答案所示,您应该在数据库中使用“十进制”类型来表示货币。

请尝试这是否有效:

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

如果你在输出中看到“inf”,你在某处做零除..更好地检查你的计算逻辑,并防止除零。


根据维基百科的公式是:   http://en.wikipedia.org/wiki/Amortization_calculator

为了改善舍入误差,重新构造这样的公式可能更好:

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

等于:

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

等于:(使用此一个)

 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

答案 1 :(得分:2)

我认为它与浮点运算精度有关。这里已经讨论过:Ruby number precision with simple arithmetic并且最好将十进制格式用于财务目的。

答案可能是计算循环中的数字,但是具有预先计算的迭代次数和从头开始。