众所周知,现在银行的分期贷款利率是很有诱惑性人。表面看利率是很低的,例如招行的闪电贷有时给我的利率是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)的更多相关文章

  1. R语言计算moran‘I

    R语言计算moran‘I install.packages("maptools")#画地图的包 install.packages("spdep")#空间统计,m ...

  2. R语言计算相关矩阵然后将计算结果输出到CSV文件

    R语言计算出一个N个属性的相关矩阵(),然后再将相关矩阵输出到CSV文件. 读入的数据文件格式如下图所示: R程序采用如下语句: data<-read.csv("I:\\SB\land ...

  3. R语言计算IV值

    更多大数据分析.建模等内容请关注公众号<bigdatamodeling> 在对变量分箱后,需要计算变量的重要性,IV是评估变量区分度或重要性的统计量之一,R语言计算IV值的代码如下: Ca ...

  4. [转帖]C语言计算时间函数 & 理解linux time命令的输出中“real”“user”“sys”的真正含义

    C语言计算时间函数 & 理解linux time命令的输出中“real”“user”“sys”的真正含义 https://blog.csdn.net/willyang519/article/d ...

  5. C语言计算两个日期间隔天数

    在网上看到了一个C语言计算日期间隔的方法,咋一看很高深,仔细看更高神,很巧妙. 先直接代码吧 #include <stdio.h> #include <stdlib.h> in ...

  6. 闰年计算——JavaScript 语言计算

    ㈠闰年是如何来的? 闰年(Leap Year)是为了弥补因人为历法规定造成的年度天数与地球实际公转周期的时间差而设立的.补上时间差的年份为闰年. ㈡什么是闰年? 凡阳历中有闰日(二月为二十九日)的年, ...

  7. [R语言]R语言计算unix timestamp的坑

    R+mongo的组合真是各种坑等着踩 由于mongo中的时间戳普遍使用的是unix timestamp的格式,因此需要对每天的数据进行计算的时候,很容易就想到对timestamp + gap对方式来实 ...

  8. C语言计算开方

    C语言里面有sqrt可以计算开平方根,但似乎想要计算开任意次方根的话却没有一个固定的函数,自己写算法也蛮啰嗦的…… 其实啊,巧妙使用pow函数就可以实现需求. C语言库函数pow的原型声明如下: #i ...

  9. 使用R语言-计算均值,方差等

    R语言对于数值计算很方便,最近用到了计算方差,标准差的功能,特记录. 数据准备 height <- c(6.00, 5.92, 5.58, 5.92) 1 计算均值 mean(height) [ ...

随机推荐

  1. ZT Shell 排序

    Shell 排序 分类: 算法 C 2008-09-17 11:02 1898人阅读 评论(4) 收藏 举报 shell语言c 刚才在CSDN的C语言板块看到了有人说Shell排序的问题,所以一起学习 ...

  2. React onPaste 获取粘贴板的值

    React 中, 获取 粘贴板的值, 使用下面的方法 console.log(e.clipboardData.getData('Text')); 如果是 JS 中的 onpaste 事件, 则使用 v ...

  3. Azkaban时区问题导致调度差1天

    设置了Azkaban调度是每日凌晨一次,如下: 但是调度历史上显示最近一次调度时间是 初步怀疑是因为时区问题导致,查看服务器时区如下 cat /etc/timezone 为Asia/Shanghai. ...

  4. [转]Apache HTTP Server 与 Tomcat 的三种连接方式介绍

    首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接.事实上 Tomcat 本身已经提供了 HTTP 服务,该服务默认的端口是 8080,装好 tomcat 后通过 8080 端 ...

  5. CAN--UART的协议转换器

    CAN--UART的协议转换器 //------------------------------------------------------//  CAN <==> UART的协议转换 ...

  6. html5复习--canvas

    一.简介 <canvas>是html5新增的标签,可以使用脚本(通常为JavaScript)在其中绘制图像的 HTML 元素.它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至 ...

  7. 学习JavaSE 数组

    一维数组 基本概念 1.数组中只允许放同一种类型(可以是父子关系). 2.数组即对象. 例: int[ ] arrs={0,1,2};//arrs即一个对象. 3.数组是定长的,不可以增加或者减少. ...

  8. OpenGL 混合功能

    一.概念:简言之,即在颜色缓存区和深度缓存区中,新旧颜色的覆盖和替换问题:已经存在于缓存区的为目标颜色,即将进入缓存区的为源颜色: 二.应用场景:在不透明的图形前绘制一个透明的图形: 三.主要代码实现 ...

  9. Echarts根据数据长度变换柱状图柱状的颜色

    //查询图表数据            function GetData() {                var qs = $("#qs").val();          ...

  10. 关于Quartus+Modelsim 门级仿真 Warning (vopt-2216) Cannot find instance 'NA' specified in sdf.的解决办法

    本文操作环境:Win 7 32位系统, Quartus II 11.1 ,Modelsim SE 10.1a 在Quartus II中调用Modelsim SE做Gate Level Simulait ...