Thread: Using integer variables instead of double to calculate compound interest

  1. #1
    Registered User
    Join Date
    Aug 2010
    Posts
    8

    Using integer variables instead of double to calculate compound interest

    The exercise:

    Modify the program in Fig. 4.6 so that it uses only integers to calculate the compound interest. (Hint: Treat all monetary amounts as integral numbers of pennies. Then "break" the result into its dollar portion and cents portion by using the division and modulus operations respectively. Insert a period.)

    Fig. 4.6:
    Code:
    /* Fig. 4.6: fig04_06.c
       Calculating compound interest */
    #include <stdio.h>
    #include <math.h>
    
    int main()
    {
       int year;
       double amount, principal = 1000.0, rate = .05;
    
       printf( "%4s%21s\n", "Year", "Amount on deposit" );
    
       for ( year = 1; year <= 10; year++ ) {
          amount = principal * pow( 1.0 + rate, year );
          printf( "%4d%21.2f\n", year, amount );
       }
    
       return 0;
    }
    Output:
    Code:
    Year           Amount on deposit
       1                     1050.00
       2                     1102.50
       3                     1157.63
       4                     1215.61
       5                     1276.28
       6                     1340.10
       7                     1407.10
       8                     1477.46
       9                     1551.33
      10                     1628.89

    My work:
    Code:
    /* Ex. 4.23: ex4_23.c
       Modify Fig. 4.6 using integers instead
       of double variable to calculate compound interest */
    #include <stdio.h>
    
    
    int main()
    {
       int year, amount = 1, rate = 5, digits, deci, expo;
    
       printf( "%4s%21s\n", "Year", "Amount on deposit" );
    
       rate += 100;   // 1.05 taken as 105 in integer
    
       // calculate compound interest for a duration of 10 years
       for ( year = 1; year <= 10; year++ )
       {
          // find the power of rate, using expo as exponential value
          for ( expo = year; expo >= 1; expo-- )
          {
             amount *= rate;
    
             //reduce the value of power to a six digit number
             while ( amount >= 999999 )
             {
                amount /= 10;
             }
          }
    
          /* while the value of power is less than 6 digits,
          multiply by 10 till it's a six digits number */
          while ( amount <= 100000 )
             amount *= 10;
    
          digits = amount / 100;   // break amount into dollar portion
          deci = amount % 100;     // break amount into cents portion
    
          // if the decimal is not a two digit figure, concatenate with a zero
          if ( deci <= 10)
             printf( "%4d%18d.0%d\n", year, digits, deci );
          else
             printf( "%4d%18d.%d\n", year, digits, deci );
    
          amount = 1;  // reinitialize power to 1 to use again
       }
    
       return 0;
    }
    Output:
    Code:
    Year           Amount on deposit
        1                    1050.00
        2                    1102.50
        3                    1157.62
        4                    1215.50
        5                    1276.27
        6                    1340.08
        7                    1407.08
        8                    1477.43
        9                    1551.30
       10                    1628.86


    I don't get the exact value as the double variables program. Is the logic that I have used good?
    Last edited by dev123; 08-07-2010 at 11:35 AM.

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Well I did some minimal research and found that compound interest is more like a series of simple interest contracts in that the interest you've paid in previous periods becomes part of the principle in the next periods. It seems that this is how your program treats the problem, except it's doing something with the effective yield on the interest. The simple interest calculation may be easier to implement as there are no exponents to replace.

    The hardest part is correctly figuring the interest rate without the help of decimal points (still).

    I'm sorry I can't be of more help. I'm notoriously bad at math.

  3. #3
    Registered User
    Join Date
    Jun 2008
    Posts
    62
    Looks like a rounding issue to me. Try going out to 3 or decimal places instead of the standard 2.

  4. #4
    Registered User
    Join Date
    Jul 2010
    Location
    Oklahoma
    Posts
    107
    Dev,

    I agree with Cogman. There is a rounding issue with your implementation. I also have concerns about how you arrived at you "six-digit" figure for adjusting for the polynomial expansion of the (1+r) binomial. I took some notes from what you have, and got out the Calculus book....


    d1*10^n + ... + d4*10^3 + d3*10^2 + d2*10^1 + d1*10^0
    |--------dollars--------------| |-----pennies----------| ^ fraction of a penny
    x = [ d1, d2, d3, ..., dn ]

    Compound interest formula:
    A(t) = P*(1 + r)^t
    1000*A(t) = 1000*P*(1 + r)^t

    Since r is a real number 0 < r < 1, the formula must be adjusted in order to
    implement it using integers.
    Thus let r = R/100, and substitute:

    1000*A(t) = 1000*P*(1+R/100)^t

    Now we have a function that produces only integer results using only
    integer parameters....

    How much of the result must be scaled as a result of this though? The code
    you submitted did account for the extra factor of 100 that had been applied
    to the second term of the binomial (namely r), but merely counting the
    digits lost some precision as you noticed from the output.

    Since t is an integer number of years, I am going to expand the binomial
    using Pascal's triangle:

    t = 1 ==> 1+ R/100
    t = 2 ==> 1+ 2R/100+ R^2/10000
    t = 3 ==> 1+ 3R/100+ 3R^2/10000+ R^3/1000000
    t = 4 ==> 1+ 4R/100+ 6R^2/10000+ 4R^3/1000000+ R^4/100000000
    ...

    As you can see the size of the divisor quickly becomes too large for the
    integer precision. According to limits.h the maximum value is: 2147483647
    And for that matter we are really only interested in the closest fraction
    of a penny.

    So at year t, our issue has become negotiating the size and accuracy of the
    binomial expansion of (1 + R/100)^t. Expanding the expression isn't easily
    implemented computationally either, so back to the Algebra:

    1000*A(t) = 1000*P*((100+R)/100)^t
    1000*A(t) = 1000*P*(100+R)^t/100^t
    1000*A(t) = (10^3/100^t)*P*(100+R)^t
    1000*A(t) = (10^3/(10^2)^t)*P*(100+R)^t
    1000*A(t) = (10^3/(10^2t)*P*(100+R)^t
    1000*A(t) = 10^(3-2t)*P*(100+R)^t

    Since you were discussing using only integers, I'm not certain that my solution is what you meant to have, but the inverse operation is unavoidable while calculating the interest. Even your implementation lost digits in the "bit bucket" when scaling the "amount" variable back after calculating the exponent. This is what I adapted from your requirements:

    Code:
    double myPow( int base, int exp );
    
    int main()
    {
        int principle;
        int amount;
        int rate;
        int year;
        
        int dollars;
        int cents;
        
        principle = 1000; // P = $1000.00 ==> P = principle
        rate = 5;    // r = 0.05 or 5% = rate/100
        
        for( year = 1; year <= 10; year++ )
        {
            // gives 1000*A(t)
           amount = myPow(10, 3 - 2*year)* principle * myPow(100 + rate, year);
    
           if( 5 <= (amount % 10) )
           {
                  amount += 10;
           }
           // gives 100*A(t)
           amount /= 10;
           
           // gives the dollar portion
           dollars = amount / 100;
           // gives the fraction of a dollar
           cents = amount % 100;
           
           // if the decimal is not a two digit figure, concatenate with a zero
           if ( cents < 10 )
              printf( "%4d%18d.0%d\n", year, dollars, cents );
           else
              printf( "%4d%18d.%d\n", year, dollars, cents );
    
       }
    
       return 0;
    }
    
    double myPow( int base, int exp )
    {
       double result = 1.0;
       
       while( 0 < exp )
       {
          result *= (double)base;
          exp--;
       }
       
       while( 0 > exp )
       {
          result /= (double)base;
          exp++;
       }
    
       return result;
    }
    Perhaps rather than using the double to perform a division (since it's less than one), you could use the (3-2t) to control the division by ten loop when you scale the exponent. I hope this helps...I really like mathematics and I recall some of the underclass people working on this specific problem. Their resolution was that division by ten alluded to some property of real numbers...later in abstract algebra and real analysis it was identified as part of the closure property of groups. The inverse operation with respect to multiplication.

    Does that look right to you? The output is:

    Code:
       1              1050.00
       2              1102.50
       3              1157.63
       4              1215.51
       5              1276.28
       6              1340.10
       7              1407.10
       8              1477.46
       9              1551.33
      10              1628.89
    Best Regards,

    New Ink -- Henry

  5. #5
    Registered User
    Join Date
    Aug 2010
    Posts
    8
    Thanks for your reply to both.

    To Cogman:
    Is there a way to round an integer value? For example, round the number 13456 to a four digits which will give 1346.

    To New Ink
    Henry, your logic is good but the function myPow return a double value. The exercise asked to use only integers to calculate the compound interest.
    Last edited by dev123; 08-09-2010 at 01:32 PM.

  6. #6
    Registered User
    Join Date
    Jun 2009
    Posts
    486
    Code:
    int my_round(int n)
    {
        return (n+5)/10
    }
    Something like that will return the number rounded to one decimal place less than given, with the last digit cut off. It's not at all efficient, but it is intuitive.

    ei


    my_round(4)=0
    my_round(5)=1
    my_round(94)=9
    my_round(95)=10
    my_round(949)=95
    my_round(953)=95
    my_round(957)=96


    I'll leave it to you to generalize and make it only round off the number of decimal places you want.
    Last edited by KBriggs; 08-09-2010 at 01:46 PM.

  7. #7
    Registered User
    Join Date
    Jul 2010
    Location
    Oklahoma
    Posts
    107
    Dev,

    I was brainstorming about that the last day or two. It does use a double. The numerical analysis involved is rather intense too, I'll have to go to the library and read about the sound approaches to the problem. This is a version that calculates it with the integer constraints you mentioned. The problem that I'm having is predicting the number of significant digits which should be applied to the next multiplication in order to maintain the desired accuracy. Here have a look:

    Code:
    /*
    ** Copyright: New Ink -- Henry
    ** For display and educational purposes at cprogramming.com.
    **
    ** Problem statement:
    ** Calculate compound interest (to the nearest whole percent), using
    ** only integer data types.
    **
    ** Compound interest formula:
    ** at = a0 * (1 + r)^t
    ** with at, a0 and r elements of the (two decimal place [2D]) real numbers and
    ** t an element of the natural numbers.
    **
    ** Let:
    ** a0 = round(A0 / 1000),
    ** r = R / 100, and
    ** at = round(At / 1000)
    **
    ** Substituting:
    ** At/1000 = A0/1000 * (1 + R/100)^t
    ** with At, A0, R and t elements of the natural numbers 
    ** At = 10^(-2t) * A0 * (100 + R)^t
    **
    ** Now to manage the precision on the interest compounded:
    ** Since, (100 + R)^t will grow rather quickly, it is advantageous to 
    ** calculate how many digits are participating in the multiplication.
    **
    ** t = 1 ==> 105 (both decimal places) or 1.05
    ** t = 2 ==> 11025 (four decimal places) or 1.1025
    ** t = 3 ==> 1157625 (six decimal places) or 1.157625 of which only six
    ** participate in the calculation to the nearest fraction of a penny.
    ** t = 4 ==> 121550625 (eight decimal places) or 1.21550625 of which only six
    ** pariticpate...
    **
    ** Inductively this appears as 0, 0, 0, 2, 4, 6...divisions by 10 as t is 
    ** increased by one. Since, 2n produces the even number sequence, offset it
    ** it by three even numbers to produce 2n - 6, or -4, -2, 0, 2, 4.  Because, -4, 
    ** -2 and 0 are not larger than the loop variable the loop does not run.
    **
    ** Taking this into account with the 3 signficant digits on the amount,
    ** t = 1 ==> 2D + 3D = 5D of which we only require 3
    ** t = 2 ==> 4D + 3D = 7D 
    ** t = 3 ==> 6D + 3D = 9D because we constrained the interest to three
    ** extra digits
    ** t = 4 ==> 6D + 3D = 9D...
    */
    
    #include <stdio.h>
    
    unsigned long long int myPow( int base, int exp );
    
    void printIntroduction();
    void printColumns();
    void printDecimal( int integer, int decimal );
    
    void printTable( const int duration, const int principle, const int rate );
    
    int main()
    {
        int duration;
        int principle;
        int rate;
        
        printIntroduction();
        
        principle = (1000.00f) * 1000;
        rate = (0.05f) * 100;
        duration = 10;
        
        printTable( duration, principle, rate );
        
        return 0;
    }
    
    unsigned long long int myPow( int base, int exp )
    {
       unsigned long long int result = 1ULL;
       
       while( 0 < exp )
       {
          result *= (unsigned long long int)base;
          exp--;
       }
       
       return result;
    }
    
    void printIntroduction()
    {
        fprintf( stdout, "This program calculates compound interest.\n\n" );
        
        return;
    }
    
    void printColumns()
    {
       fprintf( stdout, "%4s%21s\n", "Year", "Amount on deposit" );
    
       return;
    }
    
    void printDecimal( int integer, int decimal )
    {
        if( 0 > decimal || 99 < decimal )
        {
           fprintf( stderr, "Number format error: %d.%d\n", integer, decimal );
        }
        else if( 10 > decimal )
        {
           fprintf( stdout, "%18d.0%d", integer, decimal );
        }
        else
        {
           fprintf( stdout, "%18d.%d", integer, decimal );
        }
        
        return;
    }
    
    void printTable( const int duration, const int principle, const int rate )
    {
        unsigned long long int result;
        int year;
        int k;
    
        printColumns();
    
        for( year = 1; year <= duration; year++ )
        {
            result = ((unsigned long long int)myPow( 100 + rate, year ));
            
            // truncate extra digits from the interest
            for( k = 0; k < 2*year - 6; k++ )
            {
               result /= 10ULL;
            }
            // multiply principle
            result *= ((unsigned long long int)principle);
            
            // truncate extra digits from the principle*growth
            if( 1 == year )
            {
               result /= 100ULL;
            }
            else if( 2 == year )
            {
               result /= 10000ULL;
            }
            else
            {
               result /= 1000000ULL;
            }
            
            // round to the nearest fraction of a penny
            if( 5 <= ( result % 10 ) )
            {
               result += 10ULL;
            }
            
            // truncate the fraction of a penny
            result /= 10ULL;
            
            fprintf( stdout, "%4d", year );
            printDecimal( result / 100ULL, result % 100 );
            fprintf( stdout, "\n" );
        }
    
        return;
    }
    The output is:

    Code:
    This program calculates compound interest.
    
    Year    Amount on deposit
       1              1050.00
       2              1102.50
       3              1157.63
       4              1215.51
       5              1276.28
       6              1340.10
       7              1407.10
       8              1477.46
       9              1551.33
      10               153.16
    As you can see the calculation for the correct number of digits is not correct for the tenth year. I recall something about an accumulator that is used in floating point arithmetic operations, and I'll have to look it up somewhere to provide any more assistance.

    As far as rounding is concerned, the method I used in this revision is summarized by:

    Code:
    int round( int num, int digits )
    {
       while(digits > 1)
       {
          num /= 10;
          digits--;
       }
    
       if( 5 <= (num % 10) )
       {
          num += 10;
       }
       num /= 10;
    
       return num;
    }
    But there's already a function built in for rounding floating point numbers, as the compiler will be happy to point out. I'm going to have to check out numerical analysis before I can be of more assistance. I'm confident that a general solution will have something to do with error propagation during multiplication. I hope that it helps. Thank you for the opportunity.

    Best Regards,

    New Ink -- Henry

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Travel Expenses-Weird Result
    By taj777 in forum C++ Programming
    Replies: 4
    Last Post: 04-06-2010, 02:23 PM
  2. Testing some code, lots of errors...
    By Sparrowhawk in forum C Programming
    Replies: 48
    Last Post: 12-15-2008, 04:09 AM
  3. need some help with last part of arrays
    By Lince in forum C Programming
    Replies: 3
    Last Post: 11-18-2006, 09:13 AM
  4. load gif into program
    By willc0de4food in forum Windows Programming
    Replies: 14
    Last Post: 01-11-2006, 10:43 AM
  5. Hi, could someone help me with arrays?
    By goodn in forum C Programming
    Replies: 20
    Last Post: 10-18-2001, 09:48 AM