Steve Summit C notes , exercise

A

arnuld

this is the programme i created, for exercise 2, assignment 3 at

http://www.eskimo.com/~scs/cclass/asgn.beg/PS2.html

it runs fine. i wanted to know if it needs any improvement:

----------------- PROGRAMME ----------------------------
/* Steve Summit's C programming

Section 3 :: exercise 2

STATEMENT:
Write a program to compute the average of the ten numbers 1, 4,
9, ..., 81, 100,
that is, the average of the squares of the numbers from 1 to 10. (This
will be a
simple modification of Exercise 3 from last week: instead of printing
each square
as it is computed, add it in to a variable sum which keeps track of
the sum of all
the squares, and then at the end, divide the sum variable by the
number of numbers summed.)

*/

#include <stdio.h>

#define DIVISOR 10

int main()
{
int i;
double sum;

sum = 0;
for(i = 0; i <= 10; ++i)
sum += i*i;

printf("the average is: %.1f\n", sum / DIVISOR);

return 0;

}

----------------------- OUTPUT ----------------------
[arch@voodo steve-summit]$ gcc -std=c99 -pedantic -Wall -Wextra
assign-3_ex-2.c
[arch@voodo steve-summit]$ ./a.out
the average is: 38.5
[arch@voodo steve-summit]$
 
R

Richard Heathfield

arnuld said:

#include <stdio.h>

#define DIVISOR 10

int main()
{
int i;
double sum;

sum = 0;
for(i = 0; i <= 10; ++i)
sum += i*i;

The loop iterates i through these values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
and 10. Now, 1 to 10 clearly gives us ten values, and 0 gives us an
eleventh, so DIVISOR should surely be 11, not 10?
 
A

arnuld

The loop iterates i through these values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
and 10. Now, 1 to 10 clearly gives us ten values, and 0 gives us an
eleventh, so DIVISOR should surely be 11, not 10?

OOPS!, thanks

i will start with "i = 1"
 
M

Martin Ambuhl

arnuld said:
it runs fine. i wanted to know if it needs any improvement:

----------------- PROGRAMME ----------------------------
/* Steve Summit's C programming

Section 3 :: exercise 2

STATEMENT:
Write a program to compute the average of the ten numbers 1, 4,
9, ..., 81, 100,
that is, the average of the squares of the numbers from 1 to 10.

/* Since the sum of the squares of the integers 1 ... N is
N * (N + 1) * (2*N + 1) / 6,
and there are N integers in 1 ... N,
we have the following general function.
It could be a macro, instead.
Note the decimal points (there's one extra one
not needed). If the function is not required
to work except with N==10, replace the returned
expression with 38.5
*/

#include <stdio.h> /* not needed for the problem as stated, but
the actal program we will show the value
computed. */

inline double average_of_squares(int n)
{
return (n + 1.) * (2. * n + 1) / 6.;
}

/& To satisfy the requirements of the program, you need only :

int main(void)
{
average_of_squares(10);
return 0;
}

but showing the value computed is probably a good idea: */


int main(void)
{
printf("The average is %g\n", average_of_squares(10));
return 0;
}
 
M

Martin Ambuhl

Richard said:
arnuld said:



The loop iterates i through these values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
and 10. Now, 1 to 10 clearly gives us ten values, and 0 gives us an
eleventh, so DIVISOR should surely be 11, not 10?

The problem was for 1 ... 10. His starting with 0 does not affect the
result; it just adds a (very small) time-wasting NOP.
 
U

user923005

/* Steve Summit's C programming
*
* Section 3 :: exercise 2
*
* STATEMENT: Write a program to compute the average of the ten
numbers 1, 4,
* 9, ..., 81, 100, that is, the average of the squares of the
numbers from
* 1 to 10. (This will be a simple modification of Exercise 3 from
last
* week: instead of printing each square as it is computed, add it in
to a
* variable sum which keeps track of the sum of all the squares, and
then at
* the end, divide the sum variable by the number of numbers summed.)
*
* I wrote it in a way that was fun for me. YMMV.
*/

#include <stdio.h>
#include <math.h>
#include <assert.h>

double geometricMean(size_t count, double vector[])
{
double sum = vector[0];
size_t index;

if (count == 0)
return 0;
for (index = 1; index < count; index++)
sum *= vector[index];

return pow(sum, 1.0 / count);
}

/* Ref: http://mathworld.wolfram.com/PowerMean.html */
/* CANNOT be used on negative quantities! */
double generalizedMean(double power, size_t count, double
vector[])
{
size_t index;
double sum = 0;

if (count == 0)
return 0;
if (power == 0)
sum = geometricMean(count, vector);
else {
for (index = 0; index < count; index++) {
assert(vector[index] > 0);
sum += pow(vector[index], power);
}
sum /= count;
sum = pow(sum, 1.0 / power);
}
return sum;
}

double arithmeticMean(size_t count, double vector[])
{
double sum = 0;
size_t index;

if (count == 0)
return 0;
for (index = 0; index < count; index++)
sum += vector[index];

return sum / count;
}

double harmonicMean(size_t count, double vector[])
{
double sum = 0;
size_t index;

if (count == 0)
return 0;
for (index = 0; index < count; index++)
sum += 1.0 / vector[index];
sum /= count;
return 1.0 / sum;
}

double rmsMean(size_t count, double vector[])
{
double sum = 0;
size_t index;

if (count == 0)
return 0;
for (index = 0; index < count; index++)
sum += vector[index] * vector[index];

sum /= count;
return sqrt(sum);
}

int main()
{
int i;
double vector[10];

for (i = 1; i <= 10; ++i) {
vector[i - 1] = i * i;
}

printf("The Arithmetical average is: %.1f\n",
generalizedMean(1.0, 10, vector));
printf("Checking.....................: %.1f\n\n",
arithmeticMean(10, vector));
printf("The Harmonic average is: %.1f\n",
generalizedMean(-1.0, 10, vector));
printf("Checking.....................: %.1f\n\n", harmonicMean(10,
vector));
printf("The Geometric average is: %.1f\n",
generalizedMean(0.0, 10, vector));
printf("Checking.....................: %.1f\n\n",
geometricMean(10, vector));
printf("The RootMeanSquare average is: %.1f\n",
generalizedMean(2.0, 10, vector));
printf("Checking.....................: %.1f\n\n", rmsMean(10,
vector));

return 0;
}

/*
C:\tmp>cl average.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42
for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.

average.c
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.

/out:average.exe
average.obj

C:\tmp>average
The Arithmetical average is: 38.5
Checking.....................: 38.5

The Harmonic average is: 6.5
Checking.....................: 6.5

The Geometric average is: 20.5
Checking.....................: 20.5

The RootMeanSquare average is: 50.3
Checking.....................: 50.3

*/
 
J

Jason Curl

Martin said:
/* Since the sum of the squares of the integers 1 ... N is
N * (N + 1) * (2*N + 1) / 6,
and there are N integers in 1 ... N,
we have the following general function.
It could be a macro, instead.
Note the decimal points (there's one extra one
not needed). If the function is not required
to work except with N==10, replace the returned
expression with 38.5
*/

#include <stdio.h> /* not needed for the problem as stated, but
the actal program we will show the value
computed. */

inline double average_of_squares(int n)
{
return (n + 1.) * (2. * n + 1) / 6.;
}

I believe for portable C, we don't want to use 'inline', unless you're
using C99. And even then, when I read the spec the it is implementation
defined (i.e. it might exist as a keyword and do nothing). Couldn't we
expect the compiler to inline this for us anyway at higher optimisation
levels?
 
J

Jason Curl

arnuld said:
this is the programme i created, for exercise 2, assignment 3 at

http://www.eskimo.com/~scs/cclass/asgn.beg/PS2.html

it runs fine. i wanted to know if it needs any improvement:

----------------- PROGRAMME ----------------------------
/* Steve Summit's C programming

Section 3 :: exercise 2

STATEMENT:
Write a program to compute the average of the ten numbers 1, 4,
9, ..., 81, 100,
that is, the average of the squares of the numbers from 1 to 10. (This
will be a
simple modification of Exercise 3 from last week: instead of printing
each square
as it is computed, add it in to a variable sum which keeps track of
the sum of all
the squares, and then at the end, divide the sum variable by the
number of numbers summed.)

*/

#include <stdio.h>

#define DIVISOR 10

int main()
{
int i;
double sum;

You can probably make this an int. The square of an int is still an int
(providing no overflows). It's generally faster using ints than doubles.
sum = 0;
for(i = 0; i <= 10; ++i)

To make your code portable, some might say to use
for (i=1; i<=DIVISOR; i++)

the ordering of ++i or i++ doesn't matter here.
sum += i*i;

printf("the average is: %.1f\n", sum / DIVISOR);

if you take my advice above about using an int above, just be careful to
promote sum to a float here, e.g. (float)sum / DIVISOR
return 0;

}

----------------------- OUTPUT ----------------------
[arch@voodo steve-summit]$ gcc -std=c99 -pedantic -Wall -Wextra
assign-3_ex-2.c
[arch@voodo steve-summit]$ ./a.out
the average is: 38.5
[arch@voodo steve-summit]$
 
J

Jason Curl

Jason said:
You can probably make this an int. The square of an int is still an int
(providing no overflows). It's generally faster using ints than doubles.


To make your code portable, some might say to use
for (i=1; i<=DIVISOR; i++)

Sorry - I meant to say 'Maintainable' not portable.
the ordering of ++i or i++ doesn't matter here.
sum += i*i;

printf("the average is: %.1f\n", sum / DIVISOR);

if you take my advice above about using an int above, just be careful to
promote sum to a float here, e.g. (float)sum / DIVISOR
return 0;

}

----------------------- OUTPUT ----------------------
[arch@voodo steve-summit]$ gcc -std=c99 -pedantic -Wall -Wextra
assign-3_ex-2.c
[arch@voodo steve-summit]$ ./a.out
the average is: 38.5
[arch@voodo steve-summit]$
 
G

Gregor H.

Write a program to compute the average of the ten numbers
1, 4, 9, ..., 81, 100, that is, the average of the squares
of the numbers from 1 to 10.

#include <stdio.h>

#define DIVISOR 10

int main()
{
int i;
double sum;

sum = 0;
for(i = 0; i <= 10; ++i)
sum += i*i;

printf("the average is: %.1f\n", sum / DIVISOR);

return 0;
}

I'd do it the following way:

ANSI-C:

#include <stdio.h>

#define NUMBERS 10

int main(void)
{
int i, sum;

sum = 0;
for(i = 1; i <= NUMBERS; ++i)
sum += i*i;

printf("The average is: %lf\n", (double) sum / NUMBERS);

return 0;
}

C-99:

#include <stdio.h>

#define NUMBERS 10

int main(void)
{
int sum = 0;

for(int i = 1; i <= NUMBERS; ++i)
sum += i*i;

printf("The average is: %lf\n", (double) sum / NUMBERS);

return 0;
}
 
G

Gregor H.

This is legal C99 (in which %lf was codified) but not legal C90.
Thanx! I overlooked that. Did a copy&paste of the OP's original code... :-(

ANSI-C:

printf("The average is: %f\n", (double) sum / NUMBERS);


G. H.
 
G

Gregor H.

A correction. The type specification for printf (in the ANSI-C program)
should read "%f" and not "%lf"!
I'd do it the following way:

ANSI-C:

#include <stdio.h>

#define NUMBERS 10

int main(void)
{
int i, sum;

sum = 0;
for(i = 1; i <= NUMBERS; ++i)
sum += i*i;

printf("The average is: %f\n", (double) sum / NUMBERS);
^^
return 0;
}


G. H.
 
S

santosh

arnuld said:
this is the programme i created, for exercise 2, assignment 3 at

http://www.eskimo.com/~scs/cclass/asgn.beg/PS2.html

it runs fine. i wanted to know if it needs any improvement:

----------------- PROGRAMME ----------------------------
/* Steve Summit's C programming

Section 3 :: exercise 2

STATEMENT:
Write a program to compute the average of the ten numbers 1, 4,
9, ..., 81, 100,
that is, the average of the squares of the numbers from 1 to 10. (This
will be a
simple modification of Exercise 3 from last week: instead of printing
each square
as it is computed, add it in to a variable sum which keeps track of
the sum of all
the squares, and then at the end, divide the sum variable by the
number of numbers summed.)

*/

#include <stdio.h>
#define DIVISOR 10

int main()
{
int i;
double sum;

sum = 0;

Just set sum to zero at initialisation.
for(i = 0; i <= 10; ++i)

i should probably start from 1. Also replace the hardcoded 10 with
DIVISOR. That was it's purpose in the first place.
 
T

Thad Smith

Martin said:
Richard said:
arnuld said [original code lines restored]:
#include <stdio.h>

#define DIVISOR 10

int main()
{
int i;
double sum;

sum = 0;
for(i = 0; i <= 10; ++i)
sum += i*i;

>>> printf("the average is: %.1f\n", sum / DIVISOR);

return 0;

}

The loop iterates i through these values: 0, 1, 2, 3, 4, 5, 6, 7, 8,
9, and 10. Now, 1 to 10 clearly gives us ten values, and 0 gives us an
eleventh, so DIVISOR should surely be 11, not 10?

The problem was for 1 ... 10. His starting with 0 does not affect the
result; it just adds a (very small) time-wasting NOP.

True, but if the intent of doing exercises is to learn to program well,
rather than getting the correct answer to a simple exercise, then it
helps to form habits that will support robustness of more complicated
programs. In this case, I think it better that the program correspond
semantically, rather than displaying the correct answer, so iterating
from 1 to 10, rather than 0 to 10, gives a better solution, in terms of
style supporting good techniques.

Another thought: the value 10 is written twice in the program, both
representing the same upper limit. Use the DRY principle -- Don't
Repeat Yourself:

for (i = 1; i <= DIVISOR; ++i)

What about the name of the upper limit? Yes, it is used as a divisor,
but the more fundamental usage is as the upper limit or number of
values. I prefer to use the more fundamental name:

#define NVALUES 10 /* number of values to be averaged */

then use it as both the for loop limit and divisor.
 
T

Thad Smith

Jason said:
arnuld wrote:

You can probably make this an int. The square of an int is still an int
(providing no overflows). It's generally faster using ints than doubles.

It certainly is sufficient for this particular exercise, but speed
shouldn't be an issue here. I consider it a matter of style. I would
use double here for this type of simple program so that I don't have to
worry about overflow with a larger limit or truncation on division.
if you take my advice above about using an int above, just be careful to
promote sum to a float here, e.g. (float)sum / DIVISOR

That works, but I use double as the preferred floating point type. If I
choose float (or long double), it is for a specific reason.
 
A

arnuld

arnuld wrote:



Just set sum to zero at initialisation.


actually, my style is to FIRST declare all the variable and then
initialize them.

i should probably start from 1. Also replace the hardcoded 10 with
DIVISOR. That was it's purpose in the first place.

i overlooked the DIVISOR here, my mistake.
 
D

Default User

arnuld said:
actually, my style is to FIRST declare all the variable and then
initialize them.

That's not a particularly good style, and doesn't work well for
aggregates.





Brian
 
C

CBFalconer

Default said:
arnuld wrote:
.... snip ...

That's not a particularly good style, and doesn't work well for
aggregates.

Actually that is an excellent style, which keeps the actual cost of
such automatic scope initializations in evidence.
 
D

Default User

CBFalconer said:
Actually that is an excellent style, which keeps the actual cost of
such automatic scope initializations in evidence.


To me, things that need initialization (other than for() control vars)
should get it at declaration time. This cuts down on errors and
improves maintenance. I'll differ with you on it and leave it at that.
As veteran programmers, I'm unlikely to change your mind, and you're
unlikely to change mine.




Brian
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top