2011-10-05 80 views
0

我正在一个客户的网站上工作,我正在写一个摊铺计划计算器在轨道上的红宝石。对于较长的贷款期限计算,它似乎并没有被打破,当余额达到0Ruby/Rails while循环不正确?

这里是我的代码:

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循环止跌不能正常工作

回答

3

在Ruby中的默认数值是Fixnum对象...例如: -

> 15/4 
=> 3 

你会看到奇怪的舍入误差,如果您尝试使用Fixnum对象的价值观和他们分开。

为了确保您在计算中使用花车的号码中至少一个必须是一个浮动

> 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 
+0

to_d是BigDecimal,但使用浮动工作,谢谢。 – aperture

+0

不错的看点:+1 :) – apneadiving

+0

谢谢,apneadiving :) – Tilo

2

我认为它与浮点运算精度有关。这里已经讨论过了:Ruby number precision with simple arithmetic,为了财务目的使用小数格式会更好。

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