使用c语言计算分期贷款折算年化收益率(内部收益率IRR*12)
众所周知,现在银行的分期贷款利率是很有诱惑性人。表面看利率是很低的,例如招行的闪电贷有时给我的利率是4.3%
但是,由于贷款是分期还本的,我手上的本金每月都在减少,到最后一个月时手上只有少量本金,但是还的利息却还是跟第一个月一样。
excel提供了一个公式叫irr,专门用来计算这种分期贷款实际利率的。
irr函数有两个参数,第一个是现金流,第二个是预估值。只要我们根据贷款情况填好总贷款金额和每月还款金额就可以算出每月的内部收益率。
月内部收益率*12就是我们的实际贷款利率。预估值一般不用填,只有irr计算失败返回#NUM!才要考虑填,具体可见office官方说明。
https://support.office.com/zh-cn/article/IRR-%E5%87%BD%E6%95%B0-64925eaa-9988-495b-b290-3ad0c163c1bc
为方便计算,我做了一个excel表格,有兴趣大家可以去下载。只要输入下面图片黄底黑体列,即可自动得出折算年利率。招行的闪电贷利率表面看是4.3%,实际年化利率是7.84%。
如果你把钱投理财产品,没有7.84%以上你实际是亏本的。
当然,本文重点不是介绍irr函数,而是我写(抄)的一个计算irr的程序(函数)。使用的是二分迭代法(网上看还有牛顿迭代法和加速迭代法,这两种需要用到数学知识)
之所以用c语言写一个,原因是我们最近项目组有一个c程序需要计算内部收益率。我从网上找了一个程序改了一下,变成一个函数,并加上注释,方便理解和调用。
程序和代码还有前面提到的表格我都已经上传,有需要可以去下载。实现代码如下:
// testirr.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <math.h> //const double zero = 1e-5; //int n; /*
//代码是在csdn上找到的 我拿下来改了一下
//https://blog.csdn.net/dinghaoseu/article/details/50322117
//这是原来的代码
double quickpow(double a, int b)
{
double ans = 1;
while(b)
{
if(b & 1) ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
} double makeans(double irr)
{
double ans = 0;
for(int i = 1; i <= n; i++)
ans += ( a[i] / (quickpow(1 + irr, i)) );
return ans;
}
*/
//计算流出Npv
double getNpvOut(double irr, const double arrOutMoney[], int n)
{
double npv = ;
double denominator = + irr; //分母
double multiplier = denominator; //乘数 for (int i = ; i <= n; ++i)
{
npv += arrOutMoney[i] / denominator; //即:arrOutMoney[i] / (quickpow(1 + irr, i)
denominator *= multiplier;
} return npv;
} //最小最大IRR(内部收益率) 用于折算年化率计算
//最小值必须大于-1 最大值1其实就足够了,折算成年化收益率是120%(国家规定利率不能超过30%)
//数字越小计算速度越快,为保险计这里填10
#define IRR_MIN -1.0f
#define IRR_MAX 10.0f //二分迭代寻找合适的irr值(如果存在多个irr,取第一次找到的值,不保证大小顺序)
//arrInOutMoney是分期现金流 nArrLen是arrInOutMoney元素个数
//arrInOutMoney[0]必须是负数,代表总分期金额(负数),后面是每期还款金额(正数)
//返回[IRR_MIN, IRR_MAX]之间的数字代表符合要求的IRR值,<IRR_MIN代表找不到合适的IRR
double binarySearchGetIrr(const double arrInOutMoney[], int nArrLen)
{
double l = IRR_MIN, r = IRR_MAX; //irr取值-1~10之间
int n = nArrLen - ;
//int nCnt = 0; while (l < r)
{
//每次从最大和最小期望IRR中间取一个数值进行npv测算
double mid = (l + r) / ;
//现在是用除法求Npv,其实可以改成用乘法求,效率会高一点
//因为计算机处理乘法速度比较快
double npvOut = getNpvOut(mid, arrInOutMoney, n); //++nCnt;
//如果结果等于0说明找到符合要求的IRR
if (fabs(npvOut + arrInOutMoney[]) <= 1e-) //double类型不能直接与0比较判断是否相等
{
//printf("nCnt = %d\n", nCnt); //经测试,一般分12期迭代次数在30~60之间
return mid;
}
//irr越大,npvOut越小,故npvOut太大时irr就应该落在mid和r之间,反之则反之
else if (npvOut > -arrInOutMoney[])
{
l = mid;
}
else
{
r = mid;
}
} //printf("nCnt = %d\n", nCnt);
//找不到返回比IRR_MIN还小的值
return IRR_MIN - ;
} //输入按月分期现金流,输出对应irr和折算年化收益率
//arrInOutMoney是分期现金流 nArrLen是arrInOutMoney元素个数(一般是7或者13)
//arrInOutMoney[0]必须是负数,代表总分期金额,后面是每期还款金额(正数)
//nCheckFlag = 0,代表直接计算irr,否则会先对数据合法性做检查
//返回0代表成功 其它代表失败 失败原因存放在errBuf(调用者需要保证至少有256个字节空间)
int GetIrrAndAnnualizedRate(const double arrInOutMoney[], int nArrLen,
OUT double *pIrr, OUT double *pAnnualizedRate,
int nCheckFlag, OUT char *errBuf)
{
double irr = ; if (nCheckFlag != )
{
double inMoney, outMoney;
int i; if (arrInOutMoney == NULL || nArrLen < )
{
strcpy(errBuf, "arrInOutMoney需要非空并且元素个数大于2个");
return -;
} inMoney = arrInOutMoney[];
outMoney = ;
for (i = ; i < nArrLen; ++i)
{
if (arrInOutMoney[i] < )
{
//不支持多次现金流入(因为没这个需求,不要浪费计算力)
outMoney = -;
break;
}
outMoney += arrInOutMoney[i];
} if (inMoney >= || (-inMoney > outMoney))
{
strcpy(errBuf, "第一个元素必须是负现金流,之后每个元素均是正现金流,"
"并且正现金流之和要大于负现金流");
return -;
}
} irr = binarySearchGetIrr(arrInOutMoney, nArrLen);
if (irr < IRR_MIN)
{
sprintf(errBuf, "%.5f(%.2f%%)~%.5f(%.2f%%)之间无法找到合适的irr,请检查现金流是否输入异常",
IRR_MIN, IRR_MIN * * , IRR_MAX, IRR_MAX * * );
return -;
} *pIrr = irr;
*pAnnualizedRate = irr * * ; return ;
} int getIrrDemo()
{
double irr, annualizedRate;
double a[ * + ];
int n, nRet;
char errBuf[]; printf("**************如果要退出,请在还款期数填0**************\n");
while ((printf("input 还款期数 n(0代表退出):")) && ~scanf("%d", &n) && n)
{
printf("n = %d\n", n);
if (n >= sizeof(a) / sizeof(a[]))
{
printf("n值太大,不支持\n");
continue;
} printf("输入分期金额(负数):");
if (scanf("%lf", &a[]) != ) {
printf("输入的金额不能包含非数字和小数点\n");
getchar();
continue;
} printf("输入%d期还款金额(正数),每输入一期按一次回车:", n);
for (int i = ; i <= n; i++)
{
scanf("%lf", &a[i]);
} nRet = GetIrrAndAnnualizedRate(a, n + , &irr, &annualizedRate, , errBuf);
if (nRet != )
{
printf("error:[%s]\n", errBuf);
continue;
} //计算irr常用的方法是迭代计算,即不断尝试可能值,根据尝试结果缩小范围,直到找到符合要求的值
//网上能找到的迭代算法有二分迭代,牛顿迭代,加速迭代,其中二分迭代最好理解,最容易开发
irr = binarySearchGetIrr(a, n + );
if (irr < IRR_MIN)
{
printf("找不到合适的irr\n");
}
else
{
printf("irr = %.6f 年化收益率(12 * irr) = %.4f%%\n", irr, annualizedRate);
}
} return ;
} int main(int argc, char *argv[])
{
getIrrDemo();
//system("pause");
return ;
}
使用c语言计算分期贷款折算年化收益率(内部收益率IRR*12)的更多相关文章
- R语言计算moran‘I
R语言计算moran‘I install.packages("maptools")#画地图的包 install.packages("spdep")#空间统计,m ...
- R语言计算相关矩阵然后将计算结果输出到CSV文件
R语言计算出一个N个属性的相关矩阵(),然后再将相关矩阵输出到CSV文件. 读入的数据文件格式如下图所示: R程序采用如下语句: data<-read.csv("I:\\SB\land ...
- R语言计算IV值
更多大数据分析.建模等内容请关注公众号<bigdatamodeling> 在对变量分箱后,需要计算变量的重要性,IV是评估变量区分度或重要性的统计量之一,R语言计算IV值的代码如下: Ca ...
- [转帖]C语言计算时间函数 & 理解linux time命令的输出中“real”“user”“sys”的真正含义
C语言计算时间函数 & 理解linux time命令的输出中“real”“user”“sys”的真正含义 https://blog.csdn.net/willyang519/article/d ...
- C语言计算两个日期间隔天数
在网上看到了一个C语言计算日期间隔的方法,咋一看很高深,仔细看更高神,很巧妙. 先直接代码吧 #include <stdio.h> #include <stdlib.h> in ...
- 闰年计算——JavaScript 语言计算
㈠闰年是如何来的? 闰年(Leap Year)是为了弥补因人为历法规定造成的年度天数与地球实际公转周期的时间差而设立的.补上时间差的年份为闰年. ㈡什么是闰年? 凡阳历中有闰日(二月为二十九日)的年, ...
- [R语言]R语言计算unix timestamp的坑
R+mongo的组合真是各种坑等着踩 由于mongo中的时间戳普遍使用的是unix timestamp的格式,因此需要对每天的数据进行计算的时候,很容易就想到对timestamp + gap对方式来实 ...
- C语言计算开方
C语言里面有sqrt可以计算开平方根,但似乎想要计算开任意次方根的话却没有一个固定的函数,自己写算法也蛮啰嗦的…… 其实啊,巧妙使用pow函数就可以实现需求. C语言库函数pow的原型声明如下: #i ...
- 使用R语言-计算均值,方差等
R语言对于数值计算很方便,最近用到了计算方差,标准差的功能,特记录. 数据准备 height <- c(6.00, 5.92, 5.58, 5.92) 1 计算均值 mean(height) [ ...
随机推荐
- MySql5.7.* 多实例安装部署
参考文献: http://blog.csdn.net/tornadojava/article/details/53318773 http://blog.csdn.net/u013948858/arti ...
- 将Python打包成可执行文件exe的心路历程
导言: 我们有时候需要将做好的Python程序打包成为一个exe , 方便我们使用,查找了资料发现 pyinstaller .py2exe,最后还是选择的pyinstaller,用的时候踩过了挺多的坑 ...
- 【转载】MySQl 数据库插入加锁分析
http://yeshaoting.cn/article/database/mysql%20insert%E9%94%81%E6%9C%BA%E5%88%B6/
- Uva10048 Audiophobia (Floyd)
题意:有一个无向带权图,求出两点之间路径的最大边权值最小能为多少. 思路:使用floyd算法跑一边以备查询,每一次跑的过程中dp[i][j]=min(dp[i][j],max(dp[i][k],dp[ ...
- Java基础加强之并发(三)Thread中start()和run()的区别
Thread中start()和run()的区别 start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法.start()不能被重复调用.run() : run()就和普通的成 ...
- 20155314 2016-2017-2 《Java程序设计》第3周学习总结
20155314 2016-2017-2 <Java程序设计>第3周学习总结 教材学习内容总结 学习目标 区分基本类型与类类型 理解对象的生成与引用的关系 掌握String类和数组 理解封 ...
- C# 通过word模板动态生成Word
object oMissing = System.Reflection.Missing.Value; Word._Application oWord = new Word.Application(); ...
- 微信小程序、安卓APP、苹果APP对比分析
今天的话题主要是关于微信小程序.安卓APP.苹果APP对比分析.既然是对比分析肯定是将它们一个一个说明. 本篇不涉及技术话题,只讲解微信小程序.安卓APP.苹果APP它们各自的优缺点及其应用场景. 一 ...
- Docker实战(五)之端口映射与容器互联
除了网络访问外,Docker还提供了两个很方便的功能来满足服务访问的基本需求:一个是允许映射容器内应用的服务端口到本地宿主主机;另一个是互联机制实现多个容器间通过容器名来快速访问. 1.端口映射实现访 ...
- Lambda表达式学习(1)
项目里面需要经常对一系列同类型集合进行操作 , 如对集合进行增加元素 , 删除集合的指定索引的元素等等.我们可以使用ArrayList来进行. 如 ArrayList stringArrayLis ...