Logarithms: a Practical Use

This is a simple little C project demonstrating a useful application of logarithms, with the bonus of a few lines demonstrating localization.

Logarithms

I'll write a full post on logarithms at a later date, but here's a brief overview. The logarithm of a number to a specified base is the power to which the base must be raised to get the number, eg if

Exponentiation

102 = 100

then the base 10 logarithm of 100 is 2:

Logarithm

log10(100) = 2

Any number can be used as the base, but the usual bases are 2, 10 and e which is the base of natural logarithms. e is an irrational number and is 2.718281828 to 9dp. The C standard library provides functions for all three of these in math.h, and scientific calculators usually provide at least base e and base 10 logarithm keys.

John Napier, inventor of logarithms

John Napier 1550-1617, discoverer of logarithms

Since their invention by John Napier in the 17th century until a few decades ago slide rules and books of log tables were used to simplify multiplication by turning it into a process of addition. Modern science, technology and engineering all depended on that simple idea.

Get Coding

This project will consist of two parts. The first is ridiculously simple and just calculates compound interest over a period of several years, firstly just calculating the final amount and secondly calculating all interim yearly amounts.

The second part will carry out the process in reverse: starting with the opening balance and an interest rate we will calculate how long it will take for our money to grow to a specified amount. This is probably one of those little bits of mathematics that makes you think "hmm, we did that in school but I'd forgotten all about it".

As I mentioned above I will also be using a bit of localization to format numbers and get the local currency symbol.

I have used interest as an obvious example but of course the principle can be applied to any form of exponential growth or decay - reproduction of bacteria, radioactive decay and so on.

Create a new folder somewhere convenient and within it create an empty file called logarithms.c, or download the zip file from the Downloads page. Then paste the following into the file.

logarithms.c

#include<stdio.h>
#include<math.h>
#include<time.h>
#include<locale.h>

char* currsym; // global variable for local currency symbol

//--------------------------------------------------------
// FUNCTION PROTOTYPES
//--------------------------------------------------------
void calculateamounts(double startamount, double interest, int years);
void timetoamount(double startamount, double interest, double requiredamount);

//--------------------------------------------------------
// FUNCTION main
//--------------------------------------------------------
int main()
{
    // get set up for local number formatting and currency symbol
    setlocale(LC_ALL, "");
    const struct lconv* const currentlocale = localeconv();
    currsym = currentlocale->currency_symbol;

    puts("Code in C - Logarithms\n----------------------\n");

    calculateamounts(1000, 1.1, 12);

    //timetoamount(1000, 1.1, 3138.43);

    return EXIT_SUCCESS;
}

Note that locale.h is #included in this file. We'll use it to obtain the local currency symbol for the system the program is running on, as well as the local number formatting. This is what the first three lines of main achieve, and the currency symbol is stored in a global char* so it can be used across different functions.

Next we'll do the calculateamounts function. Paste the following below the existing code.

logarithms.c

//--------------------------------------------------------
// FUNCTION calculateamounts
//--------------------------------------------------------
void calculateamounts(double startamount, double interest, int years)
{
    // calculate and display amount after specified number of years
    double currentamount = startamount;
    double endamount = startamount * pow(interest, years);

    // calculate amounts for each year
    printf("startamount %s%'.2lf\n", currsym, startamount);
    printf("years %d\n", years);
    printf("interest %.2lf%%\n", (interest - 1) * 100);
    printf("endamount %s%'.2lf\n\n", currsym, endamount);

    for(int y = 1; y <= years; y++)
    {
        currentamount*= interest;

        printf("Year %2d: %s%'.2lf\n", y, currsym, currentamount);
    }
}

We can now build and run the program. In your terminal type

Compiling

gcc logarithms.c -std=c11 -lm -o logarithms

IMPORTANT: as we are using math.h you need to include -lm. Run the program with

Running

./logarithms

and you should see

Program Output

Code in C - Logarithms
----------------------

startamount £1,000.00
years 12
interest 10.00%
endamount £3,138.43

Year 1: £1,100.00
Year 2: £1,210.00
Year 3: £1,331.00
Year 4: £1,464.10
Year 5: £1,610.51
Year 6: £1,771.56
Year 7: £1,948.72
Year 8: £2,143.59
Year 9: £2,357.95
Year 10: £2,593.74
Year 11: £2,853.12
Year 12: £3,138.43

All of the above is pretty trivial, but note the use of currsym, and in particular note this:

Number formatting example

%'.2lf

The apostrophe causes the number to be printed with local formatting applied, and the .2 causes the number to be printed with two decimal places.

Finally lets get to the main purpose of this little program: given a certain amount of money earning a certain amount of interest, how long will it take for the balance to grow to a certain amount? This is calculated by taking the logarithm of the new amount as a proportion of the original amount, and dividing it by the logarithm of the growth rate. That sentence probably doesn't make sense, even if you understood how to do it beforehand, so here it is as a formula.

Formula for yearstorequiredamount

yearstorequiredamount = log(requiredamount / startamount) / log(interest)

Using the values hardcoded into main, we already know that in 12 years £1,000 will grow to £3,138 at 10%pa. (If you know where I can get 10% interest please let me know immediately!) Plugging those numbers into the formula we get

Formula for yearstorequiredamount - example

yearstorequiredamount = log(3138.43 / 1000) / log(1.1)
= log(3.13843) / log(1.1)
= 0.496712446 / 0.041392685
= 12.00000546

The rounding error is less than 3 minutes so probably not worth worrying about, but basically we have turned the process round and calculated what we already knew, that it takes 12 years to get to the final amount from our starting point at the specified interest rate.

In math.h the log function is the natural logarithm (base e) while log10 and log2 are of course bases 10 and 2. It doesn't matter which you use as we are calculating ratios rather than actual amounts, but you must use the same one both times. If you have a minute to spare try out all three, and then try breaking the code by mixing up different log functions.

Let's finish of this program with the timetoamount function.

logarithms.c

//--------------------------------------------------------
// FUNCTION timetoamount
//--------------------------------------------------------
void timetoamount(double startamount, double interest, double requiredamount)
{
    double yearstorequiredamount = log(requiredamount / startamount) / log(interest);

    printf("\nstartamount %s%'.2lf\n", currsym, startamount);
    printf("interest %.2lf%%\n", (interest - 1) * 100);
    printf("requiredamount %s%'.2lf\n", currsym, requiredamount);
    printf("yearstorequiredamount %lf\n\n", yearstorequiredamount);
}

Run the program again and you will see

Program Output

Code in C - Logarithms
----------------------

startamount £1,000.00
years 12
interest 10.00%
endamount £3,138.43

Year 1: £1,100.00
Year 2: £1,210.00
Year 3: £1,331.00
Year 4: £1,464.10
Year 5: £1,610.51
Year 6: £1,771.56
Year 7: £1,948.72
Year 8: £2,143.59
Year 9: £2,357.95
Year 10: £2,593.74
Year 11: £2,853.12
Year 12: £3,138.43

startamount £1,000.00
interest 10.00%
requiredamount £3,138.43
yearstorequiredamount 12.000005

Please let me know in the comments if you have any suggestions for improvements or enhancements, and please follow Code in C on Twitter for future posts.

This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to Logarithms: a Practical Use

  1. Jonathan says:

    Please don’t mash your variable names together. Instead of

    yearstorequiredamount

    use either ‘camel case’

    yearsToRequiredAmount

    or ‘snake case’

    years_to_required_amount

    They make programs so much easier to read.

    • Chris Webb says:

      Fair point Jonathan. Most of my variables tend to be 1 or 2 short words but that one grew to be rather unwieldy, Chris.

Leave a Reply

Your email address will not be published. Required fields are marked *