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

1. ## 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?

2. 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. Looks like a rounding issue to me. Try going out to 3 or decimal places instead of the standard 2.

4. 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

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.

6. 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.

7. 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