Starting from:
$30

$24

Week07 Lab Exercise


Lab Objective: In this week’s lab we will look at writing a program that reads a large matrix of numbers and then reports all numbers that are equal to a reference value (or within a tolerance of that value).

Here's a quick summary of the tasks:
    1. Create a directory (folder) for this week’s work
    2. Begin work on the findvals program . In doing this you will learn about
        a. Dealing with 2-D arrays or matrices of numbers in a C program

        b. Converting a string of characters that represents a number into a numerical representation

        c. Reading a number from the keyboard
        d. The trick in UNIX known as file redirection

        e. How to allocate space in your program at run-time to accommodate a large array

        f. How to print out date and time in a C program and how to time how long a program runs for

    3. In the Sulis Assignment Attachments I have provided you with an executable that will be the definitive judge in any disputes concerning formatting of output, etc. I have also left there some sample input for your program and, for the curious, a perl program that generates this sample input.

    4. When you have completed your program and when your output matches my output exactly you should hand in your program for marking

In Detail

You can create this week’s lab directory with

mkdir ~/cs4023/labs/week07

Now change your working directory to this since all of our compiling, etc. will be done in this directory.

The goal of this week’s program is to write a program that reads a massive array of numbers and reports all numbers that are within a given tolerance of a given reference value. The reference value and the tolerance will be read from the command-line. Here’s a typical use of the program:

findvals -r -6.77 -t 0.25

This should report a count of all of the numbers encountered that are within ±0.25 of -6.77,
the given reference value. Before going any further have a poke around (in Linux) this
week’s reference files and run the sample executable there on some of the sample inputs.
(Note that you may get different results from your program if you run it on a 32-bit machine or a 64-
bit machine even if you use the same input data. This is because of the differing numerical precision

between the two machine types.) Make sure and have a look at the input files that are given
there. *For example, inside the uploaded reference files, after downloading and copying to
your Linux directory, you might want to try: -
ls -l ~/cs4023/labs/week07

Followed by say:

~/cs4023/labs/week07/findvals -r 5.6 -t 23.0 < ~/cs4023/labs/week07/mat.2x3

and then just for comparison

~/cs4023/labs/week07/findvals -r 5.6 -t 230.0 < ~/cs4023/labs/week07/mat.2x3

The format of the output that you see will be the format I expect so please pay close attention to how the output is formatted.

Where to start? From last time you should be able to test for the arguments -r and -t using strcmp(). (I haven’t said which order the two come in, though and you should be able to handle either order.)

Converting strings into numbers

From last time we also know that in the above example argv[2], the third element of argv, will contain a memory address. At this memory address will be the string of characters -

6.77. Our first job is to convert the string “-6.77” to the number -6.77. For this we need the C library function strtof() that converts a string to a floating point no. This function returns a floating point no. that the string of characters represents. Please do

man strtof

…and read the first couple paragraphs

You are now in a position to write the first couple of lines of the program that processes the command-line arguments and figures out ref, the reference value of interest to us, and tol, the acceptable tolerance.

The next task is to read the matrix of numbers to search.

Reading a number

When you read an input value into a variable – say, x – you are inserting some of the contents of the input stream into the memory location that we call x. The C function to read from the keyboard is scanf(). It takes two arguments: the first one tells it what type of thing it should be reading (int, float, double, char, etc.) and then it needs to be told where to put the thing. For the latter you might think that you should give x but on a moment’s reflection you will realise that what it needs is where to put it so what scan needs as its second argument is the address of x. This is achieved with &x.

The “address of” operator “&” and the “pointer dereference” operator “*” are fellow travellers.
Input from Keyboard

To read a value from the keyboard into an int variable you need to specify where the result should go – that is, the address in memory of where the identifier is located. If x is of type int, then to read a value into x we write:

scanf("%d", &x)

To read a floating-point number into y we write:

scanf("%f", &y)

Input redirection

But where do you get your data stream from? I have made no mention of reading a file given as a command line argument...

Enter one of UNIX’s most powerful facilities. If your program expects data to be entered from the keyboard then, with the assistance of the shell, we can fool our program into thinking it is reading from the keyboard. This is known as input redirection. So rather than having to bother with opening and reading filenames specified on the command-line we can just rely on reading from standard input or stdin, as it is known in C programs.


Input/ Output Redirection

Suppose you have a program that reads data from the keyboard and writes results to the screen. If a lot of data needs to be entered then running the program multiple times will surely wear you out. With the help of the shell you can redirect the source of input to come from a file; likewise, you can redirect your output to a file so that you can save it for later use. In the present context, our program findvals expects input (a matrix of numbers) from the keyboard.

findvals -r -6.77 -t 0.25

However, if we have a matrix of numbers already saved in the file mtx we can do

findvals -r -6.77 -t 0.25 < mtx

and our program is happy out.

Speaking of out, we can save the output from our program with

findvals -r -6.77 -t 0.25 < mtx > results

and the output of the program is saved to the file results for later examination.

Allocating space for the matrix

There is no point in attempting to guess maximum likely sizes for the size of the matrix: we’ll get it wrong sooner or later. So, the more general solution is to wait until run-time and be told how big the array will be for this run of the program. The first input your program will receive, then, will be a pair of numbers (two ints) that let you know the size of the matrix to

follow: r rows, c columns. You should then prepare for reading those rc floating-point numbers.

In C two-dimensional arrays only exist as an array of arrays. That is, each row of the matrix we read in will be stored as a one-dimensional array of size c. To reinforce that point here’s the way that you access the element at row i and column j of the matrix arr and assign 2 to it:

arr[i][i] = 2;

So, what we do is ask the OS for a memory allocation – the system call is malloc() – that will accommodate c floats. malloc() returns a pointer to the block of memory if it can and returns 0 if it can’t. We do this r times, one for each row. But we also need to remember each row that we’ve requested space for!


Requesting an array of arrays in C

Here’s how I set up the code to request a memory allocation to store an (rct × cct) matrix of floats – note error checking if malloc() cannot satisfy a memory request:

float** rows = (float **) malloc(rct * sizeof(float *));
if (rows == 0)
{

fprintf(stderr, "Couldn’t alocate sufficient space.\n"); exit(1);
}
int i;
for (i = 0; i < rct; i++)
{
float* row = (float *) malloc(cct * sizeof(float));
if (row == 0)
{

fprintf(stderr, "Couldn’t alocate sufficient row space.\n"); exit(1);
}
rows[i] = row;
}


Reading the matrix

With all that under your belt you can now easily read in your matrix of numbers into the data structure that rows points to. You simply write two nested for-loops with indices i and j; index i ranges over the rows and, nested within, the for-loop indexed by j, ranges over each element of that row. There is a single line of code at the heart of the two nested loops and this reads a single float of the matrix:

scanf("%f", &rows[i][j];

Et voil`a. The matrix has been read into the array (of arrays) rows. Note that no matter how the data looks in the file the array gets filled as “first c values go in to first row, next c values into next row, etc.” That is, the scanf() function ignores any white space, new lines, etc. You should now be able to write code to

    • Determine from the command-line value and tolerance, the two required parameters and convert these to floating point numbers

    • Read from stdin the row and column “dimensions” of the matrix to follow

    • Allocate sufficient memory in the form of a one-dimensional array for each row and an additional vector that allows us to index these rows

    • Read in the entire matrix via two nested for-loops

Testing Each Element

You should now take your utils.c file from last time, copy it into this week’s lab directory, and add to it by writing a function approxEqual(). This function takes three arguments, the two values to compare and the allowed tolerance. Once you have read in the matrix in its entirety you will use this function in order to “iterate” over the matrix, comparing each element against the reference value and the allowable tolerance. If you get a “hit” in addition to keeping track of the number of them you should also print out the row index, column index and value where the “hit” was found.
The syntax for this is:

fprintf(stdout, "r=%d, c=%d: %.6f\n", r, c, rows[r][c]);

Printing time/date information

Although the UNIX utility time can be very useful to check the amount of time a program runs for, sometimes we want to know more specific things about how long various parts of code runs for. Or, related, we might want to log what time the program started at. Here’s some code that can achieve these tasks :


Using struct time_t in C

The following code will a) record the number of seconds elapsed since the “beginning of time”, b) convert this no. of seconds to a date and, c) print out this date.

#include <time.h>
struct tm *local;
time_t start, end;
time(&start); // read and record clock
local = localtime(&start);
printf("# Start time and date: %s", asctime(local));

You should add to this code now so that it logs the finish time also.
Submit ONLY the following 3 files on Sulis under Assignments :
findvals.c
utils.c
utils.h

Submission is due next Week Thursday (27th October 2022) 18:00




























































*Mention in your blogs what steps did you take to bring the files in Sulis to your directory (week07 folder) in your linux machine.

More products