eZ_Harry
eZ_Harry

Reputation: 826

Calculate average length of word (not precise enough)

I have written a program which takes up to 100 words and stores them in an array. It then works out the average length of the words and prints the result to stderr. I have the program working fine on all of my test scripts except one, in the given test script the average is calculated to be 4.04 but I need it to be 4.02. I am not sure why I am not getting this answer precise enough? Cheers

Program -

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *emalloc(size_t s) {
    void *result = malloc(s);
    if (NULL == result) {
        fprintf(stderr, "Memory allocation failed!\n");
        exit(EXIT_FAILURE);
    }
    return result;
}

/* n is the size of the array */
void print_array(char **a, int n, double average) {
    if (n == 0) {
        /* do nothing */
    } else {
        if (strlen(a[0]) > average) {
            fprintf(stdout, "%s\n", a[0]);
        }
        print_array(a + 1, n - 1, average);
    }
}

int main()
{
    #define SIZE 100
    char *username[100];
    char word[80];
    int num_words = 0;
    int p;
    float average = 0.0;

    /* Read words into array */


    while(1 == scanf("%s", word)) {
        username[num_words] = emalloc((strlen(word) + 1) * sizeof(word[0]));
        strcpy(username[num_words], word);
        num_words++;
    }

    /* Print out array */
    for (p = 0; p < num_words; p++) {
        average += strlen(username[p]);
    }

    average = average / num_words;

    print_array(username, num_words, average);

    if (average > 0) {
        fprintf(stderr, "%.2f\n", average);
    }

    return EXIT_SUCCESS;
}

Test file of words -

hello
hi there
The quick brown fox jumps over the lazy dog
Mary had a little lamb
Little lamb little lamb
Mary had a little lamb
Its fleece was white as snow

Everywhere that Mary went
Mary went, Mary went
Everywhere that Mary went
The lamb was sure to go

It followed her to school one day
School one day, school one day
It followed her to school one day
Which was against the rules

It made the children laugh and play
Laugh and play, laugh and play
It made the children laugh and play
To see a lamb in school 

Upvotes: 2

Views: 164

Answers (3)

David C. Rankin
David C. Rankin

Reputation: 84541

Continuing from my comments, you have several areas where you can remove unneeded code, and areas where you need to add checks to insure you do not add more words than you have pointers to hold or read more characters than you have storage for.

For example, you should never attempt to read more than num_words into your username array and you should limit the number of character you read each time to SIZE - 1 chars. You can do that with:

/* Read words into array */
while (num_words + 1 < SIZE && 1 == scanf (" %99s", word)) {

To allocate storage for each word, you need nothing more than:

    size_t len = strlen (word);
    username[num_words] = emalloc (len + 1);

You may also find it more convenient to keep a running sum to the word lengths and eliminate p altogether.

    num_words++;
    sum += len;

Avoid mixing double and float types in the same series of computations, just make average (and sum) type double.

Putting those pieces together, you could rewrite main() similar to the following:

int main (void)
{
    char *username[SIZE],
         word[SIZE];
    int  num_words = 0;
    double sum = 0.0,
           average = 0.0;

    /* Read words into array */
    while (num_words + 1 < SIZE && 1 == scanf (" %99s", word)) {
        size_t len = strlen (word);
        username[num_words] = emalloc (len + 1);
        if (!username[num_words]) {
            fprintf (stderr, "error: memory exhausted, username[%d]\n",
                        num_words);
            exit (EXIT_FAILURE);
        }
        strcpy (username[num_words], word);
        num_words++;
        sum += len;
    }
    average = sum / num_words;

    print_array (username, num_words, average);

    if (average > 0) {
        fprintf(stderr, "%.2f\n", average);
    }

    return EXIT_SUCCESS;
}

Example Use/Output

Using your input file:

$ ./bin/words_avg_len <../dat/wcwords.txt
hello
there
quick
brown
jumps
little
Little
little
little
fleece
white
Everywhere
went,
Everywhere
followed
school
School
school
followed
school
Which
against
rules
children
laugh
Laugh
play,
laugh
children
laugh
4.02

(note: 4.02 does include two commas)

As per the discussion, there is also no need to store the words in a array at all to compute the average length of the strings read from input. The only critical pieces of data are the length of each word and the number of words from which lengths have been taken.

You can shorted your code dramatically in that case to something similar to the following:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE 100

int main (void)
{
    char word[SIZE];
    int  num_words = 0;
    double sum = 0.0,
           average = 0.0;

    /* Read words into array */
    while (num_words + 1 < SIZE && 1 == scanf (" %99s", word)) {
        sum += strlen (word);
        num_words++;
    }
    average = sum / num_words;

    if (average > 0) {
        fprintf(stderr, "average: %.2f\n", average);
    }

    return EXIT_SUCCESS;
}

Example Use/Output

$ ./bin/words_avg_len_nosave <../dat/wcwords.txt
average: 4.02

(which leaves a lot less room for error :)

Upvotes: 2

Bathsheba
Bathsheba

Reputation: 234655

You are including punctuation characters (e.g. , on the line starting with "Mary went"), in the word length total.

That's enough to throw off your result.

You can eliminate such characters using the C standard library function isalpha.

Note also that you have the potential for undefined behaviour here: you could overflow your memory buffer when reading the words in. That could be having an effect here too. Why don't you compute a running average: there's no need to store the words at all?

Upvotes: 4

eZ_Harry
eZ_Harry

Reputation: 826

Solution: The script I was running contained 101 words, the array was only supposed to store 100.

Upvotes: -1

Related Questions