Website by Joshua Bleier

Module 3 - Files & Examples

This module will make use of files in module3.tar, which de-compresses into the folder week3. In this section, we'll start with the programs in week3/FILES.

Reading Values from a File with integ.c
Let's start with integ.c; this is a variation on integ_complete_fxn.c from the previous Module. Both programs still figure the area under the curve of a specific function, with increasingly smaller slices; the major difference is the conditional at the end of the do loop (Line 52). For integ_complete_fxn.c from last week, the conditional is set to 1, or TRUE, and as a result, the loop is infinite.

In this version of integ.c, the conditional terminates when the absolute value of the difference between the current and previous areas is no longer greater than some tolerance.

But where does this tolerance come from? We read it in from a file, using file read functions in the stdio.h library.

Previously, we've used the function scanf to accept user input and store it into a variable. The function fscanf behaves just like scanf, but instead of accepting user input, it accesses a value from a file (hence the 'f' in front of scanf).

But before we can scan in data from a file, we must:

  • declare a file pointer,
  • associate a file with this file pointer, and then
  • open that file for input.

In line 16 of integ.c, we declare the file pointer:

FILE* fp;

FILE is a reserved C data type indicating a file, and the * proceeding it indicates that it doesn't contain the file, but rather points to it (The * and the concept of pointers will be covered in depth in an upcoming module).

After the declaration of the file pointer fp, we assign it to a file and open the file for reading in line 20:

fp = fopen("integ.dat", "r");

fopen opens a file, and takes two arguments;

  • the first is a string indicating file name,
  • the second is a string indicating whether the file is open for reading or writing.

    The "r" in our fopen call means we'll be reading from the integ.dat, which must be located in the same directory as the executable program.

    There are other modifiers in the second argument that specify text or text & binary access, but "r" for reading and "w" for writing are satisfactory for our immediate purposes.

Lines 21, 22, and 23 all read data from the file integ.dat using the function fscanf. As mentioned above, fscanf is scanf for files; it takes the same arguments as scanf, and adds a file pointer argument to scan data from. Once fscanf reads a piece of data from the indicated file, the file pointer is incremented so the next fscanf will read the next piece of data from the file. So lines 21, 22 and 23

fscanf(fp,"%lf", &xleft);
fscanf(fp,"%lf", &xright);
fscanf(fp,"%lf", &tolerance);

read 3 consecutive real numbers (-10, 1000, and .001) from the file integ.dat and into the variables xleft, xright, and tolerance, respectively.

At this point, integ.c performs much the same as integ_complete_fxn.c from the last module, save for the calculation of the variable difference, in line 47;

  • after each iteration to calculate area, the area under the curve (the computation in line 46 which is the sum of the calculations in the for loop of lines 36-44),
  • we then calculate the difference between area and parea (the area computed from the previous iteration of the do loop);
  • finally, the conditional of the do loop in line 52 compares the absolute value of difference to the tolerance, read in via fscanf from integ.dat.

    while(fabs(difference) > tolerance);

    Once difference is less than or equal to tolerance, the do loop terminates.

    (NOTE: fabs is a function from the math.h library that finds the absolute value for floating point numbers)

A Simpler File Read with monte.c
Next, let's go over monte.c, which computes pi using the Monte-Carlo method. This is an even simpler example of reading data from a file, as we only fscanf one value from monte.dat. Lines 10 thru 14 perform all the file functions for this program.

FILE *fp;

fp = fopen("monte.dat", "r");
fscanf(fp, "%d", &maximum);
fclose(fp);

Once again, we declare the pointer to the file fp, assign to the file monte.dat for reading, and fscanf in an integer that gets stored in the variable maximum. In line 14, the fclose function resets the file pointer and unlocks the data file.

Note also a new type of statement in lines 22 and 23 within the for loop; it's an if statement, a conditional outside of a loop structure.

The if statement (and it's sister satements, if-then and if-then-else) takes the form

if <condition is TRUE> {
    statement1
       ...
    statementn
}

In our case, there is only one statement to perform if the condition is true, so we don't need the open and close curly brackets.

if(distance <= 1.0)
    inside++;

If distance is 1 or less, we increment inside.

Maximum acts as the condition value of the for loop that continually recomputes pi using the Monte Carlo method, and it can be changed by updating it's value in mote.dat. Inside the loop, random double-precision numbers are assigned to x & y; we then calculate distance based on the hypotenuese of the right triangle within the curve (c = sqrt(a-squared + b-squared)), and determine the current value of pi based on the ratio of the number of times the random numbers fall inside versus the entire number of iterations.

3 Programs that find the Root of an Algebraic Equation
In the previous section, we looked at bisect.c, which finds the root of an algebraic equation by continually bisecting 2 numbers and finding on which side of the bisection the root must be. The bisect method makes 3 assumptions:

  • The algebraic function f(x) has a root (i.e., crosses the X axis),
  • f(x) crosses the X axis in only one place, and
  • The root of f(x) is between the two initial guesses for x0 and x1.

The last assumption, that the root of f(x) resides between the initial values for x0 and x1, is eliminated in the program secant.c. This methods still requires two initial x values x0 and x1, but, by calculating the secant for the f(x0) and f(x1), and finding the root of the secant, we can still locate the root for f(x).

Take a look at this reference for the secant method to understand the mathematics; the critical computation that we iterate over is:

This computation is directly from Wolfram Research's web site, and is embedded in line 26 of the do loop:

xt = x0 - (y0/(y0-y1))*(x0 - x1);

The next interesting portion of the code has nothing to do with the computation; instead, it's the way we format the output to the screen via the printf; in order to line up output values vertically, we tab across using the printf formatting modifier \t.

In addition, we control the output of the real numbers by modifying %f with 5.3; the 5 indicates the total number of digits to be displayed, and the 3 indicates how many digits to display to the right of the decimal point. So %5.3f indicates that 5 digits (besides the decimal point) are displayed, and value is rounded to the nearest hundredth.

Finally, x0 takes the value of x1, and x1 takes the value calculated via the root of the secant, and we continue the loop, if the y value calculated is less than or equal to the input tolerance.

Find the Root with One Number
The Secant method improves on the Bisection method, as it has no requirements that the initial 2 guesses bracket the function's root.

However, we can still do better if we can remove the requirement of inputting 2 numbers, and instead start with 1 guessed number. The Newton Method (Which hinges on the insight that as the 2 guessed values approach one another, the secant of 2 numbers approaches the tangent of one number) takes one guessed value, and zeroes in on the root via the following computation (referenced on this Wolfram Research web page):

In newton.c, the above formula is expressed in lines 26 thru 28:

y = fxn(xn);
yp = fxnp(xn);
xnp1 = xn - (y/yp);

and line 29 calculates the y value to check it against the tolerance for the do loop conditional on line 33.

Besides Wolfram Research's reference material, you can also check out all three methods in this reference for root-finding methods.

Another Programming Example
The programs in the main module3 directory show more sophisticated uses of the types of C statements and structures that we've covered in the previous modules.

Let's take a look at one of these, game.c, which employs the use of a while loop, if statements, scanf to read in user input and a new system function from the stdio.h library to have one user guess a number input by another.

Game.c is a 2 player number guessing game; player 2 is prompted for, and types in, an integer for player 1 to guess at, and player 1 does the same. The program then loops until one player or the other correctly guesses their number.

Lines 6 through 8 declare the integer variables to be used; the one of most interest to us is the variable still_playing. This variable is set to 1 in line 10, and will be used in the while loop to see if we should continue or not. As long as it stays 1, we continue, but as soon as a player correctly guesses his or her number, the program sets still_playing to 0, and the program terminates at that point.

In lines 12 & 13, The program requests and scans in a number from Player 2 for player 1 to guess. Line 15 is the interesting one:

12 printf("Player 2: type in an integer for player 1 to guess.\n");
13 scanf("%d", &answer1);
14 /* We clear the display so Player 1 can't see the response */
15 system("clear");

The system function is a call that allows us to submit a string that is a UNIX shell call. In this case, we're submitting the shell command clear, which clears the screen, so that player 1 cannot see the number input by player 2.

Lines 17 thru 19 perform the same functions for the number input by player 1 for player 2 to guess.

Lines 21 thru 46 perform the primary functions of this program; each player submits their guess, and if-then statements are used to check the guess to the answer. The program tests and responds if the guess is to high or low in lines 25 thru 28 for player 1's submission. In line 29, the program tests to see if the guess actually is correct; if so, we print that fact out, and set still_playing to 0 to terminate the while loop.

The same thing then happens in lines 35 thru 45.

Note that there is no indication on which player 'wins'; in the extend section of this module, you'll be asked to think about how to update this program to indicate a winner (Or a draw, if both players guess correctly at the same point in the while loop.

Move on to the apply section now, to test how much you've been able to absorb from this section, then move on to the reflect section to briefly cover the other programs for this module, and the extend section, to consider file-checking additions to integ.c and monte.c.

Continue to the Apply Section ->