home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume2 / saverate / saverate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-07  |  9.9 KB  |  447 lines

  1. /*
  2.  * saverate.c
  3.  * dennis bednar 04 12 88
  4.  * {uunet|sundc}!rlgvax!dennis
  5.  *
  6.  * compute rate of return on savings account with equal deposits
  7.  * every period, either at the beginning or end of each period.
  8.  * "annuity due" is savings at the beginning of each period.
  9.  * "ordinary annuity" is savings at the end of each period.
  10.  *
  11.  * These are the unknowns:
  12.  *    total periods
  13.  *    payment per period (into the savings account)
  14.  *    future value
  15.  *    [optional] periods per year
  16.  *
  17.  * It will compute the interest per period, except that if the number
  18.  * of periods per year is given, then it is multiplied by that to get
  19.  * the percent per year.
  20.  *
  21.  * cmd [-de -t#] tot_periods pmt_per_period future_value [periods_per_year]
  22.  *    -d = debug
  23.  *    -e = deposit at end of each period
  24.  *    -t# = max number of times "Try to guess" the rate, default DEF_TRY,
  25.  *          (will speed up the algorithm if you have slow machine,
  26.           at the expense of less accuracy)
  27.  *
  28.  * To try:
  29.  *    cmd 4 100 430.913581 4
  30.  *    should give 12% a year, deposit at begin of each period:
  31.  *        Here you add $100 at the beginning of each quarter
  32.  *        for 4 consecutive quarter.  The money earns 3% per
  33.  *        quarter, so that the total you have (430.912) is
  34.  *        computed as follows:
  35.  *            100 * 1.03^4 = 112.55088100
  36.  *            100 * 1.03^3 = 109.2727
  37.  *            100 * 1.03^2 = 106.09
  38.  *            100 * 1.03   = 103
  39.  *        Total              = 430.91358100
  40.  *
  41.  *    cmd 4 100 418.362 4
  42.  *    should also give 12% a year, deposit at end of each period.
  43.  *
  44.  * Periods_per_year defaults to 1.
  45.  *
  46.  * Formula for "annuity due" (deposit at begin of each period):
  47.  *
  48.  *          (1+r)^(N+1) - (1+r)
  49.  * FV = pmt * ------------------    where r != 0    (1)
  50.  *           (r)
  51.  *
  52.  * FV = pmt * N                where r == 0.    (2)
  53.  *
  54.  * Formula for "normal annuity" (deposit at end of each period):
  55.  *
  56.  *          (1+r)^N - 1
  57.  * FV = pmt * ------------------    where r != 0    (3)
  58.  *           (r)
  59.  *
  60.  * FV = pmt * N                where r == 0.    (4)
  61.  *
  62.  *
  63.  * where
  64.  *    fv = future value, amount in account after N periods
  65.  *    pmt = payment at begin of each period
  66.  *    r = interest rate per period (eg, .01 means 1% interest per period,
  67.  *        this would have to be multiplied by 12 to get 12%/yr if each
  68.  *        period were one month)
  69.  *    N = total number of periods money is compounding interest.
  70.  *
  71.  * Derivation:
  72.  *    FV = (pmt * (1+r)^N) + (pmt * (1+r)^(N-1)) + ... + (pmt * (1+r)) )
  73.  *    we can factor out pmt for each term on the right, so
  74.  *    FV = pmt * [ (1+r)^N + ... + (1+r)]            (5)
  75.  *
  76.  * usual mathematical trick is to multiply both sides of (5) by (1+r),
  77.  * obtaining equation (6), then subtract (5) from (6), to obtain
  78.  * equation (7):
  79.  *
  80.  *    FV * (1+r) = pmt * [ (1+r)^(N+1) + ... (1+r)^2]        (6)
  81.  *
  82.  *    FV * r = pmt * [ (1+r)^(N+1) - (1+r)]            (7)
  83.  *
  84.  * Divide both side of (7) by r to get equation (1).  The only problem
  85.  * is that the division will blow up if r == 0, which is why equation
  86.  * (2) was mentioned.
  87.  *
  88.  * Derivation of equations (3) & (4) are to be proved by the reader,
  89.  * using a similar technique.
  90.  *
  91.  */
  92. #include <stdio.h>
  93. #include "getopt.h"
  94.  
  95. #define DEBUG 1
  96. #define FLOAT double
  97. #define STR_SAME !strcmp
  98.  
  99. extern    FLOAT    atof();
  100.  
  101. /* forward refs to non-int functions */
  102. FLOAT    get_rate();
  103. FLOAT    r_abs();
  104. FLOAT    get_fv();
  105. FLOAT    raise();
  106. FLOAT    ask_f();
  107.  
  108. char    *cmd;
  109. int    debug = 0;
  110. #define T_ANNUITY_DUE    0    /* pmt made at begin of each period */
  111. #define T_NORMAL_ANN    1    /* pmt made at end of each period */
  112. int    annuity_type = T_ANNUITY_DUE;
  113. #define DEF_TRY    100        /* default number of tries */
  114. int    MAX_TRY = DEF_TRY;
  115.  
  116. main( argc, argv )
  117.     int    argc;
  118.     char    *argv[];
  119. {
  120.     int    tot_per;    /* total periods */
  121.     FLOAT    rate,        /* rate per period, initially */
  122.         pmt,        /* payment per period */
  123.         fv;        /* future value */
  124.     int    per_per_yr,    /* periods per year */
  125.         num_opts;    /* number of options arguments */
  126.     int    yes;
  127.  
  128.     cmd = argv[0];
  129.  
  130.     num_opts= get_args( argc, argv ) - 1;
  131.  
  132.     /* skip over the options as though they weren't even there */
  133.     argc -= num_opts;
  134.     argv += num_opts;
  135.  
  136.     /* argc and argv now assume as if all options have been stripped out,
  137.      * except that argv[0] might not be the command any more.  Hence,
  138.      * refer to argv[0] via "cmd" saved above.
  139.      */
  140.     if (argc >= 4)
  141.         {
  142.         tot_per = atoi( argv[1] );
  143.         pmt = atof( argv[2] );
  144.         fv = atof( argv[3] );
  145.         if (argc > 5)
  146.             usage();
  147.         else if (argc == 5)
  148.             per_per_yr = atoi( argv[4] );
  149.         else
  150.             per_per_yr = 1;
  151.         }
  152.     else if (argc == 1)    /* just the command and possible args, no numbers */
  153.         {
  154.         tot_per = ask_i( "Total Number of Periods" );
  155.         pmt = ask_f( "Payment Deposited Per Period" );
  156.         fv = ask_f( "Future Value (after the last Period)" );
  157.         per_per_yr = ask_i( "Number of Periods Per year" );
  158.         if (annuity_type == T_ANNUITY_DUE)
  159.             {
  160.             yes = ask_yn( "Payments at begin of each period" );
  161.             if (!yes)    /* switch if wrong */
  162.                 annuity_type = T_NORMAL_ANN;
  163.             }
  164.         else
  165.             {
  166.             yes = ask_yn( "Payments at end of of each period" );
  167.             if (!yes)
  168.                 annuity_type = T_ANNUITY_DUE;
  169.             }
  170.         }
  171.     else
  172.         usage();
  173.  
  174.     /* common processing to do the computations */
  175.     rate = get_rate( tot_per, pmt, fv, annuity_type );
  176.     rate *= (FLOAT) per_per_yr;
  177.     rate *= (FLOAT)100.0;
  178.     printf("%.8f%% per %s, Payment deposited at %s of each period\n",
  179.     rate, (per_per_yr == 1) ? "period" : "year",
  180.     (annuity_type == T_ANNUITY_DUE) ? "beginning" : "end" );
  181.  
  182.     exit(0);
  183. }
  184.  
  185. usage()
  186. {
  187.     fprintf(stderr, "Usage: %s [-de -t#] [tot_periods pmt_per_period future_value [periods_per_year]]\n", cmd);
  188.     fprintf(stderr, "\t-d = debug\n");
  189.     fprintf(stderr, "\t-e = each payment is at the end of each period (default = begin)\n");
  190.     fprintf(stderr, "\t-t50 = sets the number of Tried guesses to 50, default = %d\n", DEF_TRY);
  191.     fprintf(stderr, "\t is used to make the program run faster, but less accuracy\n");
  192.     exit(1);
  193. }
  194.  
  195.  
  196. /*
  197.  * compute rate per period by using a binary search
  198.  */
  199. FLOAT
  200. get_rate( tot_per, pmt, fv, ann )
  201.     int    tot_per;
  202.     FLOAT    pmt,
  203.         fv;
  204.     int    ann;    /* annuity type */
  205. {
  206.     FLOAT    lo_rate,
  207.         hi_rate,
  208.         mid_rate,
  209.         try_fv;        /* guessed future value */
  210.     int    try;
  211.  
  212.     /* added this because "saverate 4 100 400 4" was returning
  213.      * .00000073% per year mysteriously.
  214.      */
  215.     if (fv == get_fv( tot_per, (FLOAT)0.0, pmt, ann))
  216.         return 0.0;
  217.  
  218.     lo_rate = .01;    /* 1% per period */
  219.     hi_rate = .05;    /* 5% per period */
  220.  
  221.     /* adjust lo_rate down until below fv */
  222.     /* this is because we may have "lost" money */
  223.     while( (try_fv = get_fv( tot_per, lo_rate, pmt, ann )) > fv )
  224.         {
  225.         lo_rate -= .05;
  226. #ifdef DEBUG
  227.         if (debug)
  228.         printf("get_rate: lowering lo_rate to %.2f\n", lo_rate);
  229. #endif
  230.         }
  231.  
  232.     /* adjust hi_rate up until above fv */
  233.     while( (try_fv = get_fv( tot_per, hi_rate, pmt, ann )) < fv )
  234.         {
  235.         hi_rate += .05;
  236. #ifdef DEBUG
  237.         if (debug)
  238.         printf("get_rate: raising hio_rate to %.2f\n", hi_rate);
  239. #endif
  240.         }
  241.  
  242.     /* binary search */
  243.     for ( try = 1; mid_rate = (lo_rate+hi_rate)/2.0; ++try)
  244.         {
  245.         try_fv = get_fv( tot_per, mid_rate, pmt, ann );
  246. #ifdef DEBUG
  247.         if (debug)
  248.         printf("get_rate: trying %lf <= %lf <= %lf\n", lo_rate, mid_rate, hi_rate);
  249. #endif
  250.         if (try_fv < fv )    /* mid_rate is too low ? */
  251.             lo_rate = mid_rate;
  252.         else
  253.             hi_rate = mid_rate;
  254.         if (r_abs( fv - try_fv ) <= .0000000001)
  255.             break;
  256.         else if (try >= MAX_TRY)
  257.             {
  258. #ifdef DEBUG
  259.             if (debug)
  260.                 {
  261.                 printf("Breaking out of loop after %d times: lo=%f, hi = %f\n", try, lo_rate, hi_rate);
  262.                 printf("hi - lo = %f\n", hi_rate - lo_rate);
  263.                 }
  264. #endif
  265.             break;
  266.             }
  267.         }
  268.     return mid_rate;
  269. }
  270.  
  271. /*
  272.  * real number absolute value
  273.  */
  274. FLOAT
  275. r_abs( f )
  276.     FLOAT    f;
  277. {
  278.     if (f >= 0)
  279.         return f;
  280.     else
  281.         return -f;
  282. }
  283.  
  284. /*
  285.  * compute future value using the formula at the beginning of this program
  286.  */
  287. FLOAT
  288. get_fv( tot_per, rate, pmt, ann )
  289.     int    tot_per;
  290.     FLOAT    rate,
  291.         pmt;
  292.     int    ann;    /* annuity type */
  293. {
  294.     FLOAT    one_r,    /* 1+r */
  295.         fv,    /* future value to return */
  296.         num,    /* numerator */
  297.         den;    /* denominator */
  298.  
  299.     /* money is not earning any interest, so prevent "divide by zero" */
  300.     if (rate == 0.0)
  301.         return (pmt * tot_per);
  302.  
  303.     one_r = (FLOAT)1.0 + rate;
  304.     if (ann == T_ANNUITY_DUE)    /* save at begin of each period */
  305.         {
  306.         num = raise( one_r, (tot_per+1) ) - one_r;
  307.         den = rate;
  308.         fv = pmt * (num / den );
  309.         return fv;
  310.         }
  311.     else if (ann == T_NORMAL_ANN)    /* save at end of each period */
  312.         {
  313.         num = raise( one_r, tot_per) - 1.0;
  314.         den = rate;
  315.         fv = pmt * (num / den );
  316.         return fv;
  317.         }
  318.     else
  319.         error("Bad arg (%d) to get_fv()\n", ann);
  320. }
  321.  
  322. /*
  323.  * return f^n
  324.  * where f is FLOAT and n is integer >= 0.
  325.  */
  326. FLOAT
  327. raise( f, n )
  328.     FLOAT    f;
  329.     int    n;
  330. {
  331.     FLOAT    rtn;
  332.  
  333.     if (n < 0)
  334.         error("raise: not written to handle negative exponents");
  335.     for (rtn = 1.0; n > 0; --n)
  336.         rtn *= f;
  337.     return rtn;
  338. }
  339.  
  340. #define MANYARGS msg, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
  341. typedef char    *CHARSTAR;
  342.  
  343. /*
  344.  * print error message and exit
  345.  * Error message does *not* have a newline at the end.
  346.  */
  347. error(MANYARGS)
  348.     CHARSTAR    MANYARGS;
  349. {
  350.     extern    char    *cmd;
  351.     char    buffer[BUFSIZ];
  352.     sprintf(buffer, MANYARGS);
  353.     fprintf(stderr, "%s: error: %s. Exitting.\n", cmd, buffer);
  354.     exit(1);
  355. }
  356.  
  357.  
  358. /*
  359.  * parses main() arguments, and returns index of first file
  360.  */
  361. get_args( argc, argv )
  362.     int    argc;
  363.     char    *argv[];
  364. {
  365.     char    c;
  366.     int    errflag;
  367.  
  368.     errflag = 0;
  369.  
  370.     while ((c = getopt(argc, argv, "det:")) != EOF)
  371.     switch(c){
  372.     case 'd':
  373.         debug = 1;
  374.         break;
  375.     case 'e':
  376.         annuity_type = T_NORMAL_ANN;
  377.         break;
  378.     case 't':
  379.         MAX_TRY = atoi( optarg );
  380.         break;
  381.     default:
  382.         errflag = 1;
  383.         break;
  384.     }
  385.  
  386.     if (errflag)
  387.         usage();
  388.  
  389.  
  390.     return optind;    /* index of first non-option */
  391. }
  392.  
  393. /*
  394.  * Ask a question and return an integer
  395.  */
  396. ask_i( msg )
  397.     char    *msg;
  398. {
  399.     int    rtn;
  400.     printf("%s ? ", msg );
  401.     fflush(stdout);
  402.     scanf( "%d", &rtn);
  403.     return rtn;
  404. }
  405.  
  406. /*
  407.  * Ask a question and return a floating point number
  408.  * HARD CODED TO ASSUME THAT RETURNS A DOUBLE !!
  409.  */
  410. FLOAT
  411. ask_f( msg )
  412.     char    *msg;
  413. {
  414.     double    rtn;
  415.     printf("%s ? ", msg );
  416.     fflush(stdout);
  417.     scanf( "%lf", &rtn);    /* %lf = double, %f = float */
  418.     return rtn;
  419. }
  420.  
  421. /*
  422.  * Ask a y/n question, and return 1 iff yes.
  423.  */
  424. ask_yn( msg )
  425.     char    *msg;
  426. {
  427.     char    rtn[100];
  428.  
  429.     while(1){
  430.         printf("%s ? (y/n) ", msg);
  431.         fflush(stdout);
  432.         fflush(stdin);    /* strange interaction scanf() & gets() */
  433.         gets( rtn );
  434.         switch( rtn[0] ){
  435.         case 'y':
  436.         case 'Y':
  437.         case '\0':
  438.             return 1;
  439.         case 'n':
  440.         case 'N':
  441.             return 0;
  442.         default:
  443.             continue;
  444.         }/* switch */
  445.     }  /* while */
  446. }
  447.