campo-sirio/gfm/intraux.c
alex ba237a9d91 Patch level : no patch
Files correlati     :
Ricompilazione Demo : [ ]
Commento            :
Aggiunti i sorgenti per Greenleaf Math Library (gfm.dll)


git-svn-id: svn://10.65.10.50/trunk@10079 c028cbd2-c16b-5b4b-a496-9718f37d4682
2002-02-26 12:19:02 +00:00

299 lines
7.8 KiB
C
Executable File

/* DEC *_InterestAux(fun, intper, fractper, intr, pv, pmt, fv, begend)
*
* ARGUMENT
* int fun, intper, begend;
* DEC *fractper, *intr, *pv, *pmt, *fv;
*
* DESCRIPTION
* Given four variables involved in compound interest, solves for the
* interest. The variables are the number of periods nper, the percentage
* interest rate per period intr, the present value pv, the periodic payment
* pmt, and the future value fv. begend specifies whether payments take
* place at the beginning or the end of each month.
* This auxillary function does the work of CompoundInterest,
* CompoundInterestSimple, and CompoundInterestCompound for calculating intr.
*
* SIDE EFFECTS
* Changes value of intr.
*
* RETURNS
* In case of success, intr is returned.
* If an error occurs, GM_NULL is returned.
*
* POSSIBLE ERRORS
* GM_NULLPOINTER
* GM_ARGVAL
*
*
*
* AUTHOR
* Jared Levy
* Copyright (C) 1988-1990 Greenleaf Software Inc. All rights reserved.
*
* ALGORITHM
* Solve the following formula for the unknown variable:
* 0=pv*alpha + (1+intr*begend)*pmt*[1-(1+intr)^-int(nper)]/intr +
* fv*(1+intr)^-int(nper)
* where alpha=1 for no odd period,
* alpha=1+intr*frac(nper) for an odd period with simple interest
* alpha=(1+intr)^frac(nper) for an odd period with compound interest
*
* The equation can be solved in closed form for any variable except intr,
* which requires numerical methods.
* The variables used by this routine have the following meanings:
* fun: 1 if called by ComoundInterest, 2 if by CompoundInterestSimple,
* 3 if by CompoundInterestCompound, and 0 if by AdvancePayment
* intper: integer part of nper
* fractper: fractional part of nper (if fun==2 or fun==3)
* intr: interest rate per period
* pv: present value
* pmt: period payment
* fv: future value (balloon payment)
* begend: GM_BEGIN if payments at beginning of period, GM_END if at end
* (or if fun==0, number of advanced payments ad)
* temp1, temp2: temporary DEC's
*/
#include <stdio.h>
#include "gm.h"
#include "gmsystem.h"
static void zcom(DEC *, int, int, DEC *, DEC *, DEC *, DEC *, DEC *, int);
DEC *_InterestAux(fun, intper, fractper, intr, pv, pmt, fv, begend)
int fun, intper, begend;
DEC *fractper, *intr, *pv, *pmt, *fv;
{
int i, places, ad;
mbool wfDone;
DEC dx1, *x1=&dx1, dx2, *x2=&dx2, dpx1, *px1=&dpx1;
DEC dpx2, *px2=&dpx2, dy1, *y1=&dy1, dy2, *y2=&dy2;
DEC dy0, *y0=&dy0, dxn, *xn=&dxn, dyn, *yn=&dyn;
DEC djunk, *junk=&djunk;
DEC dtemp1, *temp1=&dtemp1, dtemp2, *temp2=&dtemp2;
/* Can't have zero payments */
if (intper==0) {
_MacErr(GM_ARGVAL);
return(GM_NULL);
}
ad = begend;
/* add advance payments to pv */
if (fun==0) {
(void) ConvLongToDecimal(junk, (long) ad);
(void) _MulDec80Bit(junk, pmt, junk);
(void) _AddDec80Bit(junk, junk, pv);
pv = junk;
}
/* Check that there's a positive & a negative value among pv, pmt, fv */
if (!(_MacIsDecP(pv)||_MacIsDecP(pmt)||_MacIsDecP(fv))) {
_MacErr(GM_ARGVAL);
return(GM_NULL);
}
if (!(_MacIsDecN(pv)||_MacIsDecN(pmt)||_MacIsDecN(fv))) {
_MacErr(GM_ARGVAL);
return(GM_NULL);
}
/* if pmt==0, solve quickly */
/* 0 = pv + fv * (1 + i/100) ^ n ==>
i = (exp[ ln (fv / pv) / n] - 1) * 100 */
if (_MacIsDecZ(pmt)&&fun<=1) {
(void) _DivDec80Bit(temp1, fv, pv);
_MacDChgs(temp1);
(void) _LnDec80Bit(temp1, temp1);
(void) ConvLongToDecimal(temp2, (long) intper);
(void) _DivDec80Bit(temp1, temp1, temp2);
(void) _ExpDec80Bit(temp1, temp1);
(void) _SubDec80Bit(temp1, temp1, &decOne);
temp1->dc.id-=2;
(void) _ScaleDec80Bit(intr, temp1, wIntrPrec);
(void) _Sq5UnsTo4Uns(intr);
return(intr);
}
/* find bounds for intr */
/* initial bounds: 0 and 1 (percent) */
_MacDZero(x1);
_MacDCopy(x2, (&decOne));
zcom(y1, fun, intper, fractper, x1, pv, pmt, fv, begend);
zcom(y2, fun, intper, fractper, x2, pv, pmt, fv, begend);
if (_MacIsDecZ(y1)) {
_MacDCopy(intr,y1);
return(intr);
}
if (_MacIsDecZ(y2)) {
_MacDCopy(intr,y2);
return(intr);
}
_MacDCopy(y0,y1);
_MacDCopy(px1, x2);
_MacDCopy(px2, x1);
/* expanded bounds until zcom has different signs at bounds to find zero */
i=0;
while (!_MacIsDecN(y1)^_MacIsDecN(y2)) {
/* change boundary with smaller absolute value */
if ((CompareDecimal(x1,x2)==1)^_MacIsDecN(y1)) {
/* set x2 = 2 * x2 */
_MacDCopy(px2,x2);
_AddDec80Bit(x2,x2,x2);
zcom(y2, fun, intper, fractper,x2,pv,pmt, fv, begend);
if (_MacIsDecZ(y2)) {
_MacDCopy(intr,x2);
return(intr);
}
}
else {
/* if x1==0, set x1 = 10%, otherwise x1 = (old x1 + -100%) / 2 */
_MacDCopy(px1,x1);
if (_MacIsDecZ(x1))
(void) ConvLongToDecimal(x1, 10L);
else {
(void) _AddDec80Bit(x1,x1,&decMinusHundred);
(void) _MulDec80Bit(x1,x1,&decPoint5);
}
zcom(y1, fun, intper, fractper,x1,pv,pmt, fv, begend);
if (_MacIsDecZ(y1)) {
_MacDCopy(intr,x1);
return(intr);
}
}
/* maximum of 19 expansions */
i++;
if (i>19) {
_MacErr(GM_ARGVAL);
return(GM_NULL);
}
}
places=wIntrPrec;
if (places<GM_MINID)
places=GM_MINID;
if (places>GM_MAXID)
places=GM_MAXID;
(void) _ScaleDec80Bit(x1,x1,places);
(void) _ScaleDec80Bit(x2,x2,places);
if (CompareDecimal(x1,&decMinusHundred)==0) {
_MacDCopy(intr,x2);
return(intr);
}
/* Restrict range to smaller region with different signs */
if (_MacIsDecN(y0)^_MacIsDecN(y2))
_MacDCopy(x1,px2);
else
_MacDCopy(x2,px1);
/* Find root by bisection (Numerical Recipes in C, p. 263) */
wfDone=FALSE;
while (!wfDone) {
/* calculate new position */
(void) _AddDec80Bit(xn, x2, x1);
_HalveUnsArr(xn->dc.sl, 5);
/* (void) _SubDec80Bit(temp1, x2, x1);
(void) _SubDec80Bit(temp2, y2, y1);
(void) _DivDec80Bit(temp1, temp1, temp2);
(void) _MulDec80Bit(temp1, temp1, y1);
(void) _SubDec80Bit(xn, x1, temp1); */
/* calculate corresponding y */
zcom(yn, fun, intper, fractper, xn, pv, pmt, fv, begend);
/* check for approximate or exact zero */
if (_MacIsDecZ(yn)) {
_MacDCopy(intr,xn);
return(intr);
}
/* adjust appropriate boundary */
if (_MacIsDecN(y1)^_MacIsDecN(yn)) {
wfDone=(CompareDecimal(x2,xn)==0);
_MacDCopy(x2,xn);
_MacDCopy(y2,yn);
}
else {
wfDone=(CompareDecimal(x1,xn)==0);
_MacDCopy(x1,xn);
_MacDCopy(y1,yn);
}
}
(void) _Sq5UnsTo4Uns(xn);
_MacDCopy(intr,xn);
return(intr);
}
/* calculate function which must be zeroed to solved for intr */
static void zcom(y, fun, intper, fractper, intr, pv, pmt, fv, begend)
int fun, intper, begend;
DEC *y, *fractper, *intr, *pv, *pmt, *fv;
{
int ad;
DEC dopi, *opi=&dopi, dopitmn, *opitmn=&dopitmn;
DEC dtemp2, *temp2=&dtemp2, dopitmna, *opitmna=&dopitmna;
DEC dnintr, *nintr=&dnintr;
ad=begend;
_MacDCopy(nintr, intr);
if (intr->dc.id<=GM_IMAXID-2)
nintr->dc.id+=2;
else
(void) _MulDec80Bit(nintr,intr,&decPoint01);
(void) _AddDec80Bit(opi, &decOne, nintr);
(void) _IntPwrDec80Bit(opitmn, opi, -intper);
if (fun==0)
(void) _IntPwrDec80Bit(opitmna, opi, -(intper-ad));
(void) _MulDec80Bit(y, fv, opitmn);
if (!_MacIsDecZ(pv)) {
if (fun<=1)
_MacDCopy(temp2, pv);
if (fun==2) {
(void) _MulDec80Bit(temp2, nintr, fractper);
(void) _AddDec80Bit(temp2, temp2, &decOne);
(void) _MulDec80Bit(temp2, temp2, pv);
}
if (fun==3) {
(void) PowerDecimal(temp2, opi, fractper);
(void) _MulDec80Bit(temp2, temp2, pv);
}
(void) _AddDec80Bit(y, y, temp2);
}
if (_MacIsDecZ(nintr)) {
if (fun!=0)
(void) ConvLongToDecimal(temp2, (long) intper);
else
(void) ConvLongToDecimal(temp2, (long) (intper-ad));
(void) _MulDec80Bit(temp2, temp2, pmt);
}
else {
if (fun!=0)
(void) _SubDec80Bit(temp2, &decOne, opitmn);
else
(void) _SubDec80Bit(temp2, &decOne, opitmna);
(void) _DivDec80Bit(temp2, temp2, nintr);
(void) _MulDec80Bit(temp2, temp2, pmt);
if (begend==GM_BEGIN&&fun!=0)
(void) _MulDec80Bit(temp2, temp2, opi);
}
(void) _AddDec80Bit(y, y, temp2);
}