home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / SHDK_2.ZIP / SHFINANC.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-05-18  |  13.7 KB  |  473 lines

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