等额本息是指一种贷款的还款方式,指在还款期内,每月偿还同等数额的贷款(包括本金和利息)。
首先,需要确认的是,等额本息是一种非常优秀的还款方式。主要原因有两点,都写在名字里了:
一是“本息”,按占用本金的数量和时间来计算利息,从这个角度上来说,等额本息的利率一旦确定,借款周期无论怎么变,利率都是一样的,不用再额外去算什么综合利率;
二是“等额”,每期的还款金额一致,方便借款人记忆。
基于以上特性,银行等金融机构都爱使用等额本息,大多数时候,房贷也是按等额本息计算的,所以采用等额本息计息方式的房贷等有“月供”一说。
等额本息如何计算
等额本息每期还款额的计算公式是这样的:
其中,P是贷款本金,即如果借1万块钱,那这1万块就是贷款本金;R是月利率,即年利率除以12;N是还款期数,如果是按月还款,那就是月数。
以借10000元为例,年利率6%,12期还完,用以上公式计算,可得到每期还款的金额及包含的明细:
从表格中也可以看出,从第一期到最后一期,每期归还的本金越来越多,每期归还的利息越来越少,最后一期还完,剩余本金为0。
用HTML和JavaScript自动生成还款计划表
接下来,我们用HTML和JavaScript来生成还款计划表。大致的思路是这样:
- 写一个函数来计算等额本息算法下的每期还款额,让用户输入借款本金、借款利率、借款期数三个参数,然后系统获取到参数进行计算;
var principal = document.getElementById('money').value; //定义初始本金
var monthlyInterestRate = document.getElementById('interestRate').value/12; //定义月利率
var borrowingPeriod = document.getElementById('borrowingPeriod').value; //定义期数
var transition = Math.pow((1 + monthlyInterestRate),borrowingPeriod); //用过渡变量表示(1+R)的N次方
var monthlyRepayment = principal * monthlyInterestRate * transition / (transition - 1); //PMT公式
monthlyRepayment = monthlyRepayment.toFixed(2);
document.getElementById('monthlyRepayment').innerText = monthlyRepayment;
- 根据初始的剩余本金(即借款金额)计算第一期所还的利息,用每期还款额-第1期应还利息=第1期应还本金,然后用初始剩余本金-第1期应还本金=第1期剩余本金。
之后的每期都遵循同样的逻辑,可以用for循环来解决。
//计算当期利息、当期本金、剩余本金
var returnInterest = new Array(); //定义一个数组用来存储每期的应还利息
var returnPrincipal = new Array(); //定义一个数组用来存储每期的应还本金
var residualPrincipal = new Array(); //定义一个数组来存储每期的剩余本金
var bridge = principal; //用一个变量来存储当前剩余本金作为过渡值;
for(var i = 0;i < borrowingPeriod;i++)
{
returnInterest[i] = bridge * monthlyInterestRate; //计算当期的应还利息
returnPrincipal[i] = monthlyRepayment - returnInterest[i]; //计算当期的应还本金
residualPrincipal[i] = bridge - returnPrincipal[i]; //计算当期的剩余本金
bridge = residualPrincipal[i];
}
- 把得到的每一期明细输出到HTML,生成表格。和计算每期明细类似,同样用for循环把每期计算的结果输出到表格中:
var row1 = document.getElementById('borrowingPeriod').value; //根据期数定义行数
var col1 = 3; //定义列数为3
var div1 = document.getElementById("div1");
var tab="期数 应还金额 应还本金 应还利息 剩余本金 0 0 0 0 " + principal + " "; //设置两行表头
//自动生成表格
for(var i = 1;i<=row1;i++)
{
tab += ""; //产生行
tab += "" + i + " ";
tab += "" + monthlyRepayment + " ";
tab += "" + returnPrincipal[i-1] + " ";
tab += "" + returnInterest[i-1] + " ";
tab += "" + residualPrincipal[i-1] + " ";
}
tab+="
"
div1.innerHTML=tab;
等额本息的尾差
按照等额本息的算法,剩余本金到最后一期还完刚好是0元,且最后一期的月还款额等于上一期的剩余本金。
但实际上,用公式计算出来的月还款额是有很多位小数的。如果按四舍五入保留两位的,则会产生误差的。而其他几项明细都是基于此再进行测算得出的,到最后,月还款额的误差会传导给其他几项明细数据:
如果我们对每期还款额做四舍五入保留两位小数,对其他几项明细不做调整,则会得到这样的结果:
monthlyRepayment = monthlyRepayment.toFixed(2);
从金字塔型的小数部分可以清晰地看到误差的位数再一点点扩大,直到JavaScript保留的默认小数位数。
如何解决尾差
我们先来看看尾差的金额到底有多大,我们换一组参数,以房贷为例,借款200万人民币,30年还清,年利率5%。结果很长,我们看最后几期:
可以看出,最后一期的应还本金比上一期的剩余本金已经少了2元多,这两块多最后也被剩到末期的剩余本金里了,即没有还清。
当然,对于一笔30年200万的贷款,2元的尾差其实也不算多。
到这里我们已经发现,在每期还款额固定且四舍五入的情况下,一定会产生误差。我们可以这样处理,先在前端把结果调成看起来正常的样子:即最后一期应还本金等于上一期剩余本金,且最后一期剩余本金为0。
//手动算最后一期,抹除尾差
returnPrincipal[returnPrincipal.length-1] = residualPrincipal[residualPrincipal.length-2];
returnInterest[returnInterest.length-1] = (monthlyRepayment - returnPrincipal[returnPrincipal.length-1]).toFixed(2);
residualPrincipal[residualPrincipal.length-1] = 0;
然后把这部分看不见的尾差存到专门的尾差户,多收的话到期再返回给借款人,少收的话到期再补收,或者干脆每期都只“五入”不做“四舍”,最后再统一返还。
当然,如果可以接受最后一期还款额跟其他不一样的话,可以直接把这部分误差加到这期的还款额上。