Pi Computed in C

From Free Knowledge Base- The DUCK Project: information for everyone
Jump to: navigation, search
Here we try out a small program that computes the value of Pi using the 
following formula:

Pi = 4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - ... )

Here is the code:

  1 #include <stdio.h>
  2 #include <math.h>

  3 int main(int argc, char *argv[]){
  4        double t, k = 3.0, l = -1.0;
  5        int i, s;

  6        if(argc < 2){
  7                fprintf(stderr, "%s <number of iterations>\n", argv[0]);
  8                exit(1);
  9        }

        
 10        t = 1.0; 
 11        for(i = 0, s = atoi(argv[1]); i < s; i++){
 12                t += l/k; 
 13                k += 2.0; 
 14                l *= -1.0; 
 15        }
 16        t *= 4; 
 17        printf("My value of Pi: %1.6f, math.h's value of Pi: %.16f\n", t, M_PI); 
 18        printf("Absolute difference: %.16f\n", fabs(M_PI - t)); 

 19        return 0; 
 20 }

To compile the program, remove all lines numberings, those are just there to 
make it easier to go through the code. Then type in your shell, provided that 
you save the code in a file called pi.c:

 % gcc -o pi pi.c -lm

We compile pi.c to a binary called pi. -lm tells the linker (a part of the 
compiler) that we want to use math libraries. Using the -lm flag is not always 
neseccary though. Anyway, to run the program type the following in your shell:

 % ./pi 1000

The number 1000 is the number of iterations we would like to do in the main 
loop, i.e. how many items we want to use in the formula above. You should get 
something like this:

 $ ./pi 1000
 My value of Pi: 3.142592, math.h's value of Pi: 3.1415926535897931
 Absolute difference: 0.0009990007497471

Lets dissect the code. On line 1 and 2 we include some standard libraries.

  1 #include <stdio.h>
  2 #include <math.h>

stdio.h contains standard input/output functions such as printf and alike. 
math.h contains math functions such as sin, fabs, etc. What are those .h files 
anyway? It demands some explanation. Assume you would like to use some 
function like printf in your program. This function is nothing you have written 
yourself, the function comes in compiled form in your standard libraries. When 
the program is compiled a program called the linker (often ld, try man ld) 
tries to figure out how to call functions in these standard libraries. This is 
something that more or less just happens, as a new C programmer this is nothing 
you have to worry about. What is important here is that the compiler wants to 
know what printf looks like, i.e. how many arguments it wants, return type etc. 
Information about this is included in the .h files, which often is referred to 
as a header file (.h for header). We often talk about function prototypes, i.e. 
a description of the function. But why? So that the compiler can complain when 
you try to use a function is some way that is considerer incorrect, using the 
wrong number of arguments for instance. Hope that sorts it out.

What about this like then?

  3 int main(int argc, char *argv[]){

All C programs are composed of functions more or less. The compiler looks for 
a function called main. When the program is executed, that is by your shell, 
control is transfered from the shell to the place where main starts. I will 
later try (supposed I have some spare time) to explain how to write a shell, 
something that is easier than it might seem. Also there are two arguments to 
this function. int argc, char *argv[]. int argc is the number of arguments to 
the program and char *argv[] are those arguments. Arguments? These are the ones 
you provided via the command line when you started the program, "1000" as an 
example. However the programs name is always the first argument. If we look at 
the example where we did:

 % ./pi 1000

Then *argv[] looks like {"./pi", "1000"}. That also means that argc = 2, NOT 1 
as you might expect. That means that the first argument that you are likely to 
be interested in is argv[1]. As an exercise you could try to write a program 
that prints it's own name.

Here we declare some variables that we use later on:

  4        double t, k = 3.0, l = -1.0;
  5        int i, s;

Nothing much about these really, just keep in mind that variables should be 
declared in the beginning of each code block. That is the beginning of 
functions, loops, if statements etc. Quiet frankly everything that looks like 
this is valid syntax:

 {
     int a, ...
 }

Newer versions of gcc allows you to declare variables everywhere, but try to 
avoid that. You probably want your program to be compatible with older versions 
of gcc and other compilers.

Here we check that the user have supplied the correct number of arguments to 
the program:

  6        if(argc < 2){
  7                fprintf(stderr, "%s <number of iterations>\n", argv[0]);
  8                exit(1);
  9        }

        

Recall that argc is the number of arguments to the program. If these are less 
than 2 we have a problem. We print out a message, on line 7, that tells the 
user that he/she tried to use the program in an incorrect way. Then we exit the 
program with the value 1. Why 1? I'll explain this at the end of this example, 
bare with me for a while. We use the function fprintf which could be 
interpreted as: File-PRINT-Format. FILE here refers to the file stderr, which 
is a standard file of type FILE*. See the example ConnectingASocketInC for a 
better explanation about this. PRINT simply refers to printing, that is writing 
something to the shell. Format means that want to format the output in some 
sense. The %s in "%s <number of iterations>" refers to a string, that is the 
second argument given to fprintf, namely argv[0]. Check your manpages for 
fprintf (that is man fprintf) for a lot more details.

Simply set t to 1.0.

 
 10        t = 1.0; 

This for-loop can be interpreted as follows. First set i to 0, then s to 
atoi(argv[0]). Observe that we do both these things before the first ;. This 
part is just done once, that is when we enter the loop. What is this atoi? The 
argument "1000" in the example above, i.e. the number of iterations we want to 
do, comes as a string, not a number. Therefor we have to convert this string 
to a number. atoi does this, (man atoi).

This loop will loop as long as i is less than s. That is i < s. This test is 
done at the beginning of the loop. In every iteration of the loop i will be 
incremented by one. This is done at the end of the loop.

 11        for(i = 0, s = atoi(argv[1]); i < s; i++){

Here, compute t += l/k. This corresponds to some random part of the formula, 
for example: -1/3 or 1/5.

 12                t += l/k; 

Increment k by two, so if k = 3 before this, then k = 5 after this.

 13                k += 2.0; 

Multiply l by -1.0, something that simply changes the sign of l. -1*-1 = 1, 
math you know.

 14                l *= -1.0; 

This loop is as said repeated say 1000 times, after that we multiply the result 
by 4, according to the formula.

 16        t *= 4; 

Now lets print the result. Here again we use formated printing. But where is 
the stderr thing? Shouldn't we write to a file? Well this is done implicitly. 
Since C programmers like to print a lot, there is a standard function printf 
that is the equivalent of fprintf(stdout, ..., ...). We could as well have 
written fprintf(stdout, "My value of Pi ... ", ...);

 17        printf("My value of Pi: %.16f, math.h's value of Pi: %.16f\n", t, M_PI); 
 18        printf("Absolute difference: %.16f\n", fabs(M_PI - t)); 

What is this format %.16f? f is for float, we want to print a floating point 
number, also we want 16 significant decimals. This .16 thing is just a way to 
tell the how many decimals se want, nice huh? M_PI is a constant from math.h 
that simply is set to the value of Pi, using as high accuracy as the computer 
allows. We also use the function fabs that calculates the absolute value of a 
value. fabs could well be something like:

 double fabs(double a){
     if(a >= 0.0){
         return a; 
     }
     return -a; 
 }

At last we return 0. What is the deal with returning things? Is there any 
function that checks the value? I haven't written anyone anyway. Simply put, if 
you return anything from main or use the function exit(value); This value is 
returned to the shell. The return value is normally interpreted in fashion that 
0 means, "everything is alright, program did NOT crash", everything else means 
"The program crashed.". The return value is a way to tell the shell how things 
went wrong. What a value of 23 would mean is quiet undefined, it's more or less 
up to the program to decide that it means. But it's quiet useful when a 
shellscript executes a program. For example, shellscript start_daemon.sh tries 
to start a random daemon, like a mail server, if something goes wrong the error 
code can be used to print a message "/var/tmp is full, free at least 10 Mb 
space.".

 19        return 0;