home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 15 / CD_ASCQ_15_070894.iso / vrac / sk210f.zip / SHFINANC.PAS < prev    next >
Pascal/Delphi Source File  |  1994-05-15  |  14KB  |  487 lines

  1. {$I SHDEFINE.INC}
  2.  
  3. {$I SHUNITSW.INC}
  4.  
  5. {V-}
  6. unit ShFinanc;
  7. {
  8.                                 ShFinanc
  9.  
  10.                       A Financial Calculation Unit
  11.  
  12.                                    by
  13.  
  14.                               Bill Madison
  15.  
  16.                    W. G. Madison and Associates, Ltd.
  17.                           13819 Shavano Downs
  18.                             P.O. Box 780956
  19.                        San Antonio, TX 78278-0956
  20.                              (512)492-2777
  21.                              CIS 73240,342
  22.                 Internet bill.madison@lchance.sat.tx.us
  23.  
  24.                 Copyright 1990, '94 Madison & Associates
  25.                           All Rights Reserved
  26.  
  27.         This file may  be used and distributed  only in accord-
  28.         ance with the provisions described on the title page of
  29.                   the accompanying documentation file
  30.                               SKYHAWK.DOC
  31. }
  32.  
  33.  
  34. interface
  35.  
  36. uses
  37.   TpCrt,
  38.   TpString,
  39.   Tp8087,
  40.   ShUtilPk,
  41.   ShErrMsg;
  42.  
  43. const
  44.   Copyr = 'Copyright 1990, 1994 by W.G. Madison';
  45.  
  46. type
  47.   AnnType   = (Ordinary, Due);
  48. {$IFNDEF Gen87}
  49.   extended = real;
  50.   Float           = real;
  51. {$ELSE}
  52.   Float           = extended;
  53. {$ENDIF}
  54.  
  55. const
  56.   finOK                     = 0;
  57.   finErrParamTooSmall       = 200;
  58.   finIntOutOfRange          = 201;
  59.   finIllegalNumPeriods      = 202;
  60.   finUnknownAnnuityType     = 203;
  61.   finIllegalPresentValue    = 204;
  62.   fin80x87error             = 205;
  63.   finNoConvergence          = 206;
  64.   finIndeterminateForm      = 207;
  65.  
  66.   {80x87 errors}
  67.   finInvalidOperation       =  1;
  68.   finDenormalizedOperand    =  2;
  69.   finDivideByZero           =  4;
  70.   finOverflow               =  8;
  71.   finUnderflow              = 16;
  72.  
  73.   FW  = 17;
  74.   DP  = 10;
  75.   IW  =  6;
  76.  
  77. var
  78.   finError,
  79.   fin87error  : word;
  80.  
  81. procedure finErrCheckOn;
  82. {Turns error checking on. Errors will abort program with a message.}
  83.  
  84. procedure finErrCheckOff;
  85. {Turns error checking off. Results will be returned by function
  86.  finErrCode.}
  87.  
  88. function finErrCode : word;
  89. {Returns the error code from the last operation, and resets the error
  90.  code to zero (finOK).}
  91.  
  92. function fin87errCode : word;
  93. {Returns the 80x87 error code if finErrCode has returned fin80x87error.}
  94.  
  95. function finErrMsg(Code : word) : string;
  96. {Returns the error message corresponding to the supplied Code.}
  97.  
  98. function CompPresVal(N : integer; I : Float) : Float;
  99. {The compound present value of 1 for N periods at I.}
  100.  
  101. function CompAmount(N : integer; I : Float) : Float;
  102. {The compound amount of 1 for N periods at I.}
  103.  
  104. function AnnuityPresVal(N     : integer;
  105.                         I     : Float;
  106.                         AType : AnnType) : Float;
  107. {The present value of an annuity (of type AType) of 1 for N payment
  108.  periods at an interest rate of I per period.}
  109.  
  110. function AnnuityAmount(N : integer;
  111.                        I : Float;
  112.                        AType : AnnType) : Float;
  113. {The amount of an annuity (of type AType) of 1 for N payment periods at
  114.  an interest rate of I per period.}
  115.  
  116. function NumPay(PresVal,
  117.                 I       : Float;
  118.                 AType   : AnnType) : integer;
  119. {The number of payments needed to retire a mortgage of 1 whose present
  120.  value is PresVal at an interest rate of I per period.}
  121.  
  122. function R(Rexp : Float; Count : integer) : Float;
  123. {Returns Rexp correctly rounded to Count places to the right of the
  124.  decimal point.}
  125.  
  126. function IfromPresVal(PresVal : Float;
  127.                       N       : integer;
  128.                       AType   : AnnType;
  129.                       Err     : Float) : Float;
  130. {The interest rate of an annuity (of type AType) of 1 whose present
  131.  value is PresVal for N payments, where Err is the allowable absolute
  132.  error of calculation.}
  133.  
  134. implementation
  135.  
  136. const
  137.   HaltOnErrors  : boolean = true;
  138.   ErrorCode     : word    = 0;
  139.   Error87Code   : word    = 0;
  140.  
  141.   LoMsgNum                = 200;
  142.   HiMsgNum                = 207;
  143.   ErrMsgs       : array[LoMsgNum..HiMsgNum] of string[50] =
  144.                          ('Error parameter too small.',
  145.                           'Interest parameter out of range.',
  146.                           'Number of periods <= 0.',
  147.                           'Annuity type must be ''Ordinary'' or ''Due''.',
  148.                           'Illegal Present Value.',
  149.                           '80x87 error - ',
  150.                           'Iterative procedure; value does not converge.',
  151.                           'Indeterminate for N = 1; Type = DUE');
  152.  
  153.   Err87Msgs     : array[1..5] of string[50] =
  154.                          ('Invalid operation (e.g., LN(-1)).',
  155.                           'Denormalized operand.',
  156.                           'Divide by zero.',
  157.                           'Overflow error.',
  158.                           'Underflow error.');
  159.  
  160.   ValStr        : string  = '';
  161.  
  162. procedure finErrCheckOn;
  163. {Turns error checking on. Errors will abort program with a message.}
  164.   begin {finErrCheckOn}
  165. {$IFNDEF HaltOnFinancError}
  166.     HaltOnErrors := true;
  167.   {$IFOPT N+}
  168.     Exceptions8087(true);
  169.   {$ENDIF}
  170. {$ENDIF}
  171.     end; {finErrCheckOn}
  172.  
  173. procedure finErrCheckOff;
  174. {Turns error checking off. Results will be returned by function
  175.  finErrCode.}
  176.   begin {finErrCheckOff}
  177. {$IFNDEF HaltOnFinancError}
  178.     HaltOnErrors := false;
  179.   {$IFOPT N+}
  180.     Exceptions8087(false);
  181.   {$ENDIF}
  182. {$ENDIF}
  183.     end; {finErrCheckOff}
  184.  
  185. function finErrCode : word;
  186. {Returns the error code from the last operation, and resets the error
  187.  code to zero (finOK).}
  188.   begin {finErrCode}
  189.     finErrCode := ErrorCode;
  190. {$IFOPT N+}
  191.     if ErrorCode = fin80x87error then
  192.       Error87Code := Error8087 and $1F;
  193. {$ELSE}
  194.     Error87Code := 0;
  195. {$ENDIF}
  196.     ErrorCode := 0;
  197.     end; {finErrCode}
  198.  
  199. function fin87errCode : word;
  200. {Returns the 80x87 error code if finErrCode has returned fin80x87error.}
  201.   begin {fin87errCode}
  202.     fin87errCode  := Error87Code;
  203.     Error87Code := 0;
  204.     end; {fin87errCode}
  205.  
  206. function finErrMsg(Code : word) : string;
  207. {Returns the error message corresponding to the supplied Code.}
  208.   var
  209.     Msg1,
  210.     Msg2  : string;
  211.     C87   : word;
  212.     T1    : byte;
  213.   begin {finErrMsg}
  214.     case Code of
  215.       finOK       : Msg1 := '';
  216.       LoMsgNum..HiMsgNum
  217.                   : Msg1 := '(Error ' + Long2Str(Code) + ') ' + ErrMsgs[Code];
  218.       else          Msg1 := 'Unknown error code ' + Long2Str(Code);
  219.       end; {case}
  220.     if ValStr <> '' then begin
  221.       Msg1 := Msg1 + ValStr;
  222.       ValStr := '';
  223.       end;
  224.     Msg2 := '';
  225.     T1 := 0;
  226.     if Code = fin80x87error then begin
  227.       C87 := fin87errCode;
  228.       while C87 <> 0 do begin
  229.         inc(T1);
  230.         if (C87 and 1) <> 0 then
  231.           Msg2 := Msg2 + ^M^J^I + Err87Msgs[T1];
  232.         C87 := C87 shr 1;
  233.         end; {while}
  234.       end; {if}
  235.     finErrMsg := Msg1 + Msg2;
  236.     end; {finErrMsg}
  237.  
  238. procedure ProcessError(Code : word; Source : string);
  239.   begin {ProcessError}
  240.     if HaltOnErrors then
  241.       HaltMsg(Code, ErrMsgs[Code] + ' (' + Source + ')')
  242.     else
  243.       ErrorCode := Code;
  244.     end; {ProcessError}
  245.  
  246. function CompPresVal(N : integer; I : Float) : Float;
  247. {The compound present value of 1 for N periods at I.}
  248. var
  249.   XN  : Float;
  250. begin
  251.   if N <= 0 then begin
  252.     Str(N:IW, ValStr);
  253.     ProcessError(finIllegalNumPeriods, 'CompPresVal');
  254.     exit;
  255.     end;
  256.   if (I <= 0.0) or (I >= 1.0) then begin
  257.     Str(I:FW:DP, ValStr);
  258.     ProcessError(finIntOutOfRange, 'CompPresVal');
  259.     exit;
  260.     end;
  261.   XN := N;
  262.   CompPresVal := Exp(Ln(1.0 + I) * (-XN));
  263.   end;
  264.  
  265. function CompAmount(N : integer; I : Float) : Float;
  266. {The compound amount of 1 for N periods at I.}
  267. var
  268.   XN  : Float;
  269. begin
  270.   if N <= 0 then begin
  271.     Str(N:IW, ValStr);
  272.     ProcessError(finIllegalNumPeriods, 'CompAmount');
  273.     exit;
  274.     end;
  275.   if (I <= 0.0) or (I >= 1.0) then begin
  276.     Str(I:FW:DP, ValStr);
  277.     ProcessError(finIntOutOfRange, 'CompAmount');
  278.     exit;
  279.     end;
  280.   XN  := N;
  281.   CompAmount := Exp(Ln(1.0 + I) * XN);
  282.   end;
  283.  
  284. function AnnuityPresVal(N     : integer;
  285.                         I     : Float;
  286.                         AType : AnnType) : Float;
  287. {The present value of an annuity of 1 for N payment periods at an
  288.  interest rate of I per period.}
  289. var
  290.   CPV : Float;
  291. begin
  292.   if N <= 0 then begin
  293.     Str(N:IW, ValStr);
  294.     ProcessError(finIllegalNumPeriods, 'AnnuityPresVal');
  295.     exit;
  296.     end;
  297.   if (I <= 0.0) or (I >= 1.0) then begin
  298.     Str(I:FW:DP, ValStr);
  299.     ProcessError(finIntOutOfRange, 'AnnuityPresVal');
  300.     exit;
  301.     end;
  302.   CPV := 1.0 - CompPresVal(N, I);
  303.   case AType of
  304.     Ordinary  : AnnuityPresVal := CPV / I;
  305.     Due       : AnnuityPresVal := (1.0 + I) * CPV / I;
  306.     else        begin
  307.                   ProcessError(finUnknownAnnuityType, 'AnnuityPresVal');
  308.                   exit;
  309.                   end;
  310.     end; {case}
  311.   end;
  312.  
  313. function AnnuityAmount
  314.               (N : integer; I : Float; AType : AnnType) : Float;
  315. {The amount of an annuity of 1 for N payment periods at an
  316.  interest rate of I per period.}
  317. var
  318.   CA  : Float;
  319. begin
  320.   if N <= 0 then begin
  321.     Str(N:IW, ValStr);
  322.     ProcessError(finIllegalNumPeriods, 'AnnuityAmount');
  323.     exit;
  324.     end;
  325.   if (I <= 0.0) or (I >= 1.0) then begin
  326.     Str(I:FW:DP, ValStr);
  327.     ProcessError(finIntOutOfRange, 'AnnuityAmount');
  328.     exit;
  329.     end;
  330.   CA := CompAmount(N, I) - 1.0;
  331.   case AType of
  332.     Ordinary  : AnnuityAmount := CA / I;
  333.     Due       : AnnuityAmount := (1.0 + I) * CA / I;
  334.     else        begin
  335.                   ProcessError(finUnknownAnnuityType, 'AnnuityAmount');
  336.                   exit;
  337.                   end;
  338.     end; {case}
  339.   end;
  340.  
  341. function NumPay(PresVal, I : Float; AType : AnnType) : integer;
  342. {The number of payments needed to retire a mortgage of 1 whose present
  343.  value is PresVal at an interest rate of I per period.}
  344. begin
  345.   if (I <= 0.0) or (I > 1.0) then begin
  346.     Str(I:FW:DP, ValStr);
  347.     ProcessError(finIntOutOfRange, 'NumPay');
  348.     exit;
  349.     end;
  350.   case AType of
  351.     Ordinary  : ;
  352.     Due       : PresVal := PresVal / (1.0 + I);
  353.     else        begin
  354.                   ProcessError(finUnknownAnnuityType, 'NumPay');
  355.                   exit;
  356.                   end;
  357.     end; {case}
  358.   if (PresVal <= 0) or (PresVal >= (1.0 / I)) then begin
  359.     Str(PresVal:FW:DP, ValStr);
  360.     ProcessError(finIllegalPresentValue, 'NumPay');
  361.     exit;
  362.     end;
  363.   NumPay := -Round(Ln(1.0 - (PresVal * I)) / Ln(1.0 + I));
  364.   end;
  365.  
  366. function R(Rexp : Float; Count : integer) : Float;
  367. {Returns Rexp correctly rounded to Count places to the right of the
  368.  decimal point.}
  369. var
  370.   R1  : Float;
  371. begin
  372.   R1 := Exp(Ln(10.0) * Count);
  373.   R := Int(((Rexp * R1) + 0.5)) / R1;
  374.   end;
  375.  
  376. function IfromPresVal(PresVal : Float;
  377.                       N       : integer;
  378.                       AType   : AnnType;
  379.                       Err     : Float) : Float;
  380. {The interest rate of an ordinary annuity of 1 whose present value is
  381.  PresVal for N payments, where Err is the allowable absolute error of
  382.  calculation.}
  383.  
  384. const
  385. {$IFDEF Gen87}
  386.   MinErr = 1.0E-16;
  387. {$ELSE}
  388.   MinErr = 1.0E-9;
  389. {$ENDIF}
  390.  
  391. var
  392.   UorD    : (Up, Down);
  393.   B1      : boolean;
  394.   Last,
  395.   MErr,
  396.   Q1,
  397.   Q2,
  398.   ANI,
  399.   Intvl,
  400.   Trial   : Float;
  401.  
  402. begin
  403.   if N <= 0 then begin
  404.     Str(N:IW, ValStr);
  405.     ProcessError(finIllegalNumPeriods, 'IfromPresVal');
  406.     exit;
  407.     end;
  408.   if (N = 1) and (AType = Due) then begin
  409.     ProcessError(finIndeterminateForm, 'IfromPresVal');
  410.     exit;
  411.     end;
  412.   if Err < MinErr then begin
  413.     Str(Err:FW:DP, ValStr);
  414.     ProcessError(finErrParamTooSmall, 'IfromPresVal');
  415.     exit;
  416.     end;
  417.   if not (AType in [Ordinary..Due]) then
  418.     begin
  419.       ProcessError(finUnknownAnnuityType, 'IfromPresVal');
  420.       exit;
  421.       end;
  422.   if (PresVal <= 0) or (PresVal >= (1.0 * N)) then begin
  423.     Str(PresVal:FW:DP, ValStr);
  424.     ProcessError(finIllegalPresentValue, 'IfromPresVal');
  425.     exit;
  426.     end;
  427.   UorD := Up;
  428.   Intvl := 0.001;
  429.   Trial := 0.01;
  430.  
  431.   MErr  := -1.0 * Err;
  432.   repeat
  433.     while Intvl >= Trial do
  434.       Intvl := Intvl * 0.1;
  435.     case UorD of
  436.       Up    : begin
  437.                 while (PresVal <= AnnuityPresVal(N, Trial, AType)) and
  438.                       (Trial <= 1.0 - Intvl) do begin
  439.                   ANI := AnnuityPresVal(N, Trial, AType);
  440.                   if ANI = Last then begin
  441.                     Str(ANI:FW:DP, ValStr);
  442.                     ProcessError(finNoConvergence, 'IfromPresVal');
  443.                     exit;
  444.                     end
  445.                   else
  446.                     Last := ANI;
  447.                   Q1 := ANI / PresVal;
  448.                   Q2 := 1.0 - Q1;
  449.                   if (Q2 <= Err) and (Q2 >= MErr) then begin
  450.                     IfromPresVal := Trial;
  451.                     exit;
  452.                     end;
  453.                   Trial := Trial + Intvl;
  454.                   end;
  455.                 end;
  456.       Down  : begin
  457.                 while (PresVal > AnnuityPresVal(N, Trial, AType)) and
  458.                       (Trial >= Intvl) do begin
  459.                   ANI := AnnuityPresVal(N, Trial, AType);
  460.                   if ANI = Last then begin
  461.                     Str(ANI:FW:DP, ValStr);
  462.                     ProcessError(finNoConvergence, 'IfromPresVal');
  463.                     exit
  464.                     end
  465.                   else
  466.                     Last := ANI;
  467.                   Q1 := ANI / PresVal;
  468.                   Q2 := 1.0 - Q1;
  469.                   if (Q2 >= Err) and (Q2 <= MErr) then begin
  470.                     IfromPresVal := Trial;
  471.                     exit;
  472.                     end;
  473.                   Trial := Trial - Intvl;
  474.                   end;
  475.                 end;
  476.       end; {case}
  477.     Intvl := 0.1 * Intvl;
  478.     boolean(UorD) := not (boolean(UorD));  {Flip the value of UorD}
  479.     ANI := AnnuityPresVal(N, Trial, AType);
  480.     Q1 := ANI / PresVal;
  481.     Q2 := 1.0 - Q1;
  482.     B1 := (Q2 >= Err) and (Q2 <= MErr);
  483.     until B1;
  484.   IfromPresVal := Trial;
  485.   end; {IfromPresVal}
  486. end.
  487.