用JAVA不失精度计算等额本息还款列表

用JAVA不失精度计算等额本息还款列表

  • 还款公式推导
  • 公式
  • 代码
      • 运行结果

还款公式推导

此处纯属娱乐 可忽略

设贷款总额为A,银行月利率为β,总期数为m(个月),月还款额设为X,则各个月所欠银行贷款为:

第一个月A(1+β)-X

第二个月(A(1+β)-X)(1+β)-X=A(1+β)2-X[1+(1+β)]

第三个月((A(1+β)-X)(1+β)-X)(1+β)-X =A(1+β)3-X[1+(1+β)+(1+β)2] …

由此可得第n个月后所欠银行贷款为 A(1+β)n –X[1+(1+β)+(1+β)2+…+(1+β)(n-1)]= A(1+β)n –X[(1+β)n - 1]/β

由于还款总期数为m,也即第m月刚好还完银行所有贷款,

因此有 A(1+β)m –X[(1+β)m - 1]/β=0

由此求得 X = Aβ(1+β)m/[(1+β)m - 1]

公式

这个还是有用的,下面计算的时候会用到,尤其是每月还款额的公式,其他三个可用可不用。

每月还款额=[总本金×月利率×(1+月利率)还款月数] ÷ [(1+月利率)总期数-1]

每月应还利息=总本金×月利率×[(1+月利率)还款月数-(1+月利率)(还款月序号-1)]÷[(1+月利率)还款月数-1]

每月应还本金=总本金×月利率×(1+月利率)(还款月序号-1)÷[(1+月利率)还款月数-1]

总利息=[总期数×贷款本金×月利率×(1+月利率)还款月数]÷[(1+月利率)还款月数-1]-总本金

或者

总利息=月还本息×总期数-总本金

代码

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class InterestCalculation {
	
	static int DECIMAL_SCALE = 9;
	static BigDecimal BIGDECAMAL_100 = new BigDecimal(100);
	static BigDecimal BIGDECAMAL_12 = new BigDecimal(12);
	static BigDecimal BIGDECAMAL_30 = new BigDecimal(30);
	static BigDecimal BIGDECAMAL_360 = new BigDecimal(360);
	
	/**
	 * 等额本息 还款列表计算(自然月,即回款日期与出借日期相同)
	 * @param total	总金额
	 * @param yearRate	年利率
	 * @param sumTerm	总期数
	 * @throws Throwable
	 */
	public static void calculation_DEBX_ZRY(BigDecimal total,BigDecimal yearRate,int sumTerm) throws Throwable{
		BigDecimal monthRate = yearRate.divide(BIGDECAMAL_12,DECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);
		
		//每月还款额=[总本金×月利率×(1+月利率)^还款月数]÷[(1+月利率)^总期数-1]
		BigDecimal tmp = monthRate.add(new BigDecimal(1)).pow(sumTerm);//(1+月利率)^还款月数
		BigDecimal monthPayTotal = total
				.multiply(monthRate)
				.multiply(tmp)
				.divide(tmp.subtract(new BigDecimal(1)), 2,BigDecimal.ROUND_HALF_UP);
		
		/*总利息 
			计算总利息有3种方法:
			1.在循环中将每月应还利息累加
			2.总利息=月还本息×总期数-总本金
			3.公式:总利息=[总期数×贷款本金×月利率×(1+月利率)^还款月数]÷[(1+月利率)^还款月数-1]-总本金
			具体使用哪种根据情况而定,推荐第二种
		*/
		//2:
		BigDecimal sumInterest = monthPayTotal.multiply(new BigDecimal(sumTerm)).subtract(total);
		//3:
		/*BigDecimal sumInterest = new BigDecimal(sumTerm).multiply(total).multiply(monthRate)
				.multiply(BigDecimal.ONE.add(monthRate).pow(sumTerm))
				.divide(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE),2,BigDecimal.ROUND_HALF_UP)
				.subtract(total);*/
		
		BigDecimal remainTotal = total;	//剩余本金付初始值为总本金		
		BigDecimal monthPrincipal = BigDecimal.ZERO;
		Calendar calendar = Calendar.getInstance();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		System.out.println("本金:"+total.setScale(2)+"\t年利率:"+yearRate.multiply(BIGDECAMAL_100)+"%\t总期数:"+sumTerm+"\t总利息:"+sumInterest);
		System.out.println("期次\t回款本息(元)\t回款利息(元)\t回款本金(元)\t剩余本金(元)\t回款日期");
		for(int i=1;i<=sumTerm;i++){
			/*
			 	每月应还本金和利息也有2种方法:
			 	1.公式:
			 		每月应还利息=总本金×月利率×〔(1+月利率)^还款月数-(1+月利率)^(还款月序号-1)〕÷〔(1+月利率)^还款月数-1〕
			 		每月应还本金=总本金×月利率×(1+月利率)^(还款月序号-1)÷〔(1+月利率)^还款月数-1〕	
			 */
			/*BigDecimal interest = total.multiply(monthRate)
					.multiply(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE.add(monthRate).pow(i-1)))
					.divide(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE),2,BigDecimal.ROUND_HALF_UP);
			
			monthPrincipal = total.multiply(monthRate)
					.multiply(BigDecimal.ONE.add(monthRate).pow(i-1))
					.divide(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE),2,BigDecimal.ROUND_HALF_UP);
			*/
			
			
  			
			//2.月还利息=剩余本金×月利率
			//月还本金=月还本息-月还利息	
			//具体使用哪种根据喜好而定,第二种更容易理解
			BigDecimal interest = remainTotal.multiply(monthRate).setScale(2,BigDecimal.ROUND_HALF_UP);
			//为避免因精度损失产生误差,最后一期  还款利息=月还本息-剩余本金   
			//为避免利息产生负数的情况出现 当利息小于等于0时利息赋值为0.1元
			if (sumTerm == i) {
				interest = monthPayTotal.subtract(remainTotal);
				if(interest.compareTo(BigDecimal.ZERO)<=0){
					interest=new BigDecimal(1).divide(new BigDecimal(10),2,BigDecimal.ROUND_HALF_UP);
				}
			} 
			//月还本金=月还本息-月还利息
			monthPrincipal = monthPayTotal.subtract(interest);
			
			remainTotal = remainTotal.subtract(monthPrincipal);
			calendar.add(Calendar.MONTH, 1);
			System.out.println(i+"\t"+monthPayTotal+"\t\t"+interest+"\t\t"+monthPrincipal+"\t\t"+remainTotal+"\t\t"+sdf.format(calendar.getTime()));
		}
		
	}
	
	public static void main(String[] args) throws Throwable {
		//本金10000元,年利率15%,出借18个月
		BigDecimal total = new BigDecimal(10000);
		BigDecimal yearRate = BigDecimal.valueOf(0.15);
		int sumTerm = 18;
		calculation_DEBX_ZRY(total,yearRate,sumTerm);		
	}

运行结果

本金:10000.00	年利率:15.00%	总期数:18	总利息:1229.30
期次	回款本息(元)	回款利息(元)	回款本金(元)	剩余本金(元)	回款日期
1		623.85			125.00			498.85			9501.15			2019-06-12
2		623.85			118.76			505.09			8996.06			2019-07-12
3		623.85			112.45			511.40			8484.66			2019-08-12
4		623.85			106.06			517.79			7966.87			2019-09-12
5		623.85			99.59			524.26			7442.61			2019-10-12
6		623.85			93.03			530.82			6911.79			2019-11-12
7		623.85			86.40			537.45			6374.34			2019-12-12
8		623.85			79.68			544.17			5830.17			2020-01-12
9		623.85			72.88			550.97			5279.20			2020-02-12
10		623.85			65.99			557.86			4721.34			2020-03-12
11		623.85			59.02			564.83			4156.51			2020-04-12
12		623.85			51.96			571.89			3584.62			2020-05-12
13		623.85			44.81			579.04			3005.58			2020-06-12
14		623.85			37.57			586.28			2419.30			2020-07-12
15		623.85			30.24			593.61			1825.69			2020-08-12
16		623.85			22.82			601.03			1224.66			2020-09-12
17		623.85			15.31			608.54			616.12			2020-10-12
18		623.85			7.73			616.12			0.00			2020-11-12

还要说一下,说的不失精度只是用BigDecimal计算损失的精度较小,但是并不能保证完全不损失精度。尤其是在用年利率计算月利率的时候一定要尽可能的多保留小数位数,不然差之毫厘谬以千里啊~

本来还想算一下固定日回款的等额本息列表的,但是发现这种情况好像在实际中应用比较少,而且规则各不相同。有固定日回款本息金额与自然月回款相同只是日期延后的,有多的那几天利息在第一期算的,还有在最后一期算的,还有多的那几天利息平均到每期的。逻辑各不相同,所以写不下去了,随机应变吧~
在这里还想提醒各位小伙伴一下,有的贷款比如银行的现金分期看着像等额本息,但实际上大部分都不是按照每期的剩余本金算利息的,而是按照分期时候的全部本金算利息,而你的钱并没有全额使用那么长时间。所以,小伙伴们以后如果分期最好还是自己算一下到底划不划算~
准备下一篇写等额本金的,先息后本的太简单就不写了~

你可能感兴趣的:(JAVA)