Coding Standard For C (C++?) Programs SUMMARY OF RULES EVERY MODULE MUST BEGIN WITH A COMMENT BLOCK CONTAINING THE PURPOSE OF THE MODULE. BEGIN COMMENTS WITH A VERB. START COMMENTS WITHIN CODE EITHER IN THE SAME COLUMN AND ABOVE THE CODE IT EXPLAINS, OR TO THE RIGHT OF THE CODE. INCORRECT COMMENTS ARE OF NEGATIVE VALUE. CHOOSE MEANINGFUL VARIABLE AND PROCEDURE NAMES; DO NOT OVER-ABBREVIATE. The following variable names are in general too generic/vague. DO NOT USE THEM. [data, count, flag, stop, input, output, infile, outfile, value, returnValue, calculatedValue, answer, number1, number2]. INDENTATIONS MUST BE CONSISTENT, SAY ALWAYS 3 SPACES. ALIGN CORRESPONDING { }'S IN THE SAME COLUMN. CODE WITHIN A BOUNDING { } MUST BE ALIGNED WITH THE { }, DO NOT INDENT A SECOND TIME. If STATEMENTS: INDENT THE then PART UNDER THE if PART. If-then-else STATEMENTS: ALIGN THE KEYWORDS if AND else, AND ALIGN THE then AND else CODE. For AND while STATEMENTS: ALWAYS INDENT THE BODY OF THE LOOP. Switch STATEMENT: ALIGN THE CASES WITH THE {}'S, INDENT EACH CASE'S CODE. NEVER USE SPECIFIC NUMBERS (I.E. MAGIC NUMBERS) IN CODE; USE DEFINES. MINIMIZE, IF NOT AVOID, THE USE OF GLOBAL VARIABLES. EXCEPT FOR STATIC LOCAL VARIABLES, PUT ALL INITIALIZATION STATEMENTS IN A PROCEDURE ENTITLED "initialize". KEEP CODE READABLE; FOR EXAMPLE, IN A PRINTED LISING, LINES MUST NEVER WRAPAROUND. READABLE DOES NOT MEAN EXECUTES FASTEST. Further Details l. Comments (a) EVERY MODULE MUST BEGIN WITH A BLOCK OF COMMENTS that describes its purpose. Modules are completely documented internally, where 'completely' encompasses that amount of documentation which enables a programmer who has never seen the code before to understand the purpose without having to read the code. (Such a programmer is often the original coder six months after implementation when a problem occurs.) Therefore the main program and every module begins with the following header block of comments: 1. Purpose: what does the module do? This description is in in prose form following all rules of English grammar and punctuation. Do not describe the algorithm in step by step detail. The program description should be an overview written for someone unfamiliar with the assignment. 2. Author and Date 3. Inputs: for main program, info read in. For procedures/functions, names and descriptions of parameters passed to the procedure/function 4. Returned Values: for main program, info printed out. For a procedure, names and descriptions of affected parameters; for a function, in addition, an explanation of the returned value 5. Procedures/Functions Called: names of procedures and functions that are called from the module (with optional descriptions). Common utilities such as printf, getchar need not be listed 6. Procedures/Functions Calling: names of procedures and functions that call this module (not applicable for main program) 7. Local Variables: names and descriptions of every local variable 8. Global Variables Used (and how modified): if any, names of globals used and how the module modifies them 9. Global Constants: names and description of defined constants (found in the main program only.) 10. Limitations: known input values or conditions for which a module will not per- form properly (b) Comments explain code which is otherwise unclear. When every line of code is commented, the obvious question is "If the code is obscure enough to require a comment - shouldn't the code be rewritten?" Good code does not need hundreds of embedded comments; try to rewrite unclear code instead of commenting it. Clearly this is not always possible, and embedded comments may be necessary. BEGIN COMMENTS WITH A VERB if possible. For example, "Input number of students", "Compute mean value of score", "Convert answer to ascii". Avoid phrases such as "The following code ...", "Now we compute ...", "Lines 20 thru 28 evaluate ...", "This procedure tries to ...". START COMMENTS WITHIN CODE IN THE SAME COLUMN (AND ABOVE) THE CODE IT EXPLAINS, OR OFFSET TO THE RIGHT OF THE CODE. INCORRECT COMMENTS HAVE NEGATIVE VALUE! 2. Variable names. CHOOSE MEANINGFUL VARIABLE AND PROCEDURE NAMES; DO NOT OVER-ABBREVIATE (e.g., ttl for total, lgth for length). A good rule of thumb is 5-9 letters for every name. Single letter variable names, such as i, j, x, are acceptable only as dummy loop variables for tasks such as outputting an array. The only reason you need more than 1 dummy variable is for nested loops. 3. Indentation Opinions differ on how to best make code the most visually readable, but everyone agrees on one rule - BE CONSISTENT. a. EACH INDENTATION MUST BE 3 SPACES; not 2, not 5, always 3 b. ALIGN CORRESPONDING { }'S. ALIGN CODE WITHIN BOUNDING { } WITH THE { }, DO NOT INDENT A SECOND TIME { { Correct -----; Incorrect -----; -----; -----; { { -----; -----; -----; -----; } } } } c. If STATEMENTS: ALWAYS INDENT THE THEN PART UNDER THE IF. if (relation) if (relation) -----; { -----; -----; } d. If-then-else STATEMENTS: ALIGN KEYWORDS if AND else; ALIGN then AND else CODE if (relation) /* comment on primary relational test */ { -----; } else /* comment on purpose of initial else */ { -----; if (relation) -----; else /* comment on the purpose of the deeper else */ { -----; } } e. For AND while STATEMENTS: INDENT BODY OF THE LOOP for (i=1; i<10; i++) while (relation) array[i] = 0; { ------; ------; } f. Switch STATEMENT: ALIGN CASES WITH {}'S, INDENT EACH CASE'S CODE. The 'fall through' feature of the C switch statement should rarely if ever be used. If used, it MUST be commented for future maintenance. switch (nextchar) { case 'a': -----; -----; break; case 'b': case 'c': -----; break; default: -----; } 4. Constants and defines NEVER USE SPECIFIC NUMBERS (I.E., MAGIC NUMBERS) IN CODE; USE DEFINES. The _d_e_f_i_n_e feature of C should be used to assign a meaningful name to each constant. This facilitates large program administration as one change to the define updates the entire program. There are exceptions where 0 or 1 may appear as themselves instead of as defines. For example: Correct Incorrect #define INVALIDVALUE -999 for (i=0; i<=95; i++) #define MAXTABLESIZE 95 data[i] = -999; for (i=0; i MAXDEGREE)) { if ( degree < 0 ) printf("The only polynomial with negative degree is 0.0); else if (degree == 0) printf("Constant polynomials do not have roots.0); else printf("Polynomial too large for program.0); printf("Reenter degree between 0 and %d inclusive:0, MAXDEGREE); scanf("%d", °ree); } /* read coefficients of polynomial */ for (i=0; i<=degree; i++) { printf("Enter the coefficient of x^%d term:0, i); scanf("%f", &poly_coeffs[i]); } printf("Input lower and upper bounds of region to be searched:0); scanf("%f %f", &lower, &upper); printf("Input amount of error allowable when finding the root:0); scanf("%f", &epsilon); /* output the polynomial */ printf("0); for (i=degree; i>=0; i--) { printf("%s0, STARS ); printf("*%sCoefficent of x^%d term%s*%s%8.4f%s*0, TAB, i, TAB, TAB, poly_coeffs[i], TAB); } printf("%s0, STARS); /* check if either endpoint is a root, if not, compute roots in 3 ways*/ if (Horner(poly_coeffs, lower, degree) == 0.0) printf("Root is exactly: %f0, lower); else if (Horner(poly_coeffs, upper, degree) == 0.0) printf("Root is exactly: %f0, upper); else { Newton(poly_coeffs, lower, upper, epsilon, degree); secant(poly_coeffs, lower, upper, epsilon, degree); bisection(poly_coeffs, lower, upper, epsilon, degree); } } /********************************************************************** * procedure bisection * *********************************************************************** 1. Purpose: Bisection numerically approximates the root of a polynomial using the "bisection method". This method uses the recursive formula: mid = ( lower + upper ) / 2; if ( sign( f( lower ) ) == sign( f( mid ) ) ) lower = mid; else upper = mid; This means that an interval [lower,upper] is halved after each iteration. If the function evaluated at the half way point is the same as the lower bound, the root must lie within the interval [mid, upper]. Otherwise the root lies within the interval [lower, mid]. The variables lower and upper are reassigned to reflect this new knowledge and the process is reiterated until the difference between the upper and lower bounds is becomes less than epsilon. 2. Paul Amer, August 1984 3. Inputs: fofx -- array of coefficients of a polynomial lower -- lower endpoint of interval containing the root upper -- upper endpoint of interval containing the root epsilon -- controls accuracy of solution by bounding the minimum difference between upper and lower to be tested degree -- highest degree of all terms in the polynomial 4. Returned Values: None 5. Procedures/Functions called: Horner, sign 6. Procedures/Functions calling: main 7. Local Variables: iterations -- the number of times the bisection is performed bisect_root -- the root as computed by the bisection method mid -- the midpoint of the interval [lower, upper] 8. Global Variables Used (and how modified): None ***************************************************************************/ bisection(fofx, lower, upper, epsilon, degree) float fofx[ MAXDEGREE + 1 ], lower, upper, epsilon; int degree; { float mid, bisect_root; int iterations = 0; /* perform bisection until within tolerance of answer */ while ( upper - lower > epsilon ) { mid = ( lower + upper ) / 2; /* check signs of function values using sign function */ if (sign(Horner(fofx, mid, degree)) == sign(Horner(fofx, lower, degree))) lower = mid; else upper = mid; /* increment number of times bisection is performed */ iterations++; } /* assign and output answer */ bisect_root = ( lower + upper ) / 2; printf( "10s0, RSTARS ); printf( "*%sBISECTION - root is approximately:%s%8.4f%s*0, TAB, TAB, bisect_root, TAB ); printf( "*%sBISECTION - number of iterations:%s%d%s*0, TAB, TAB, iterations, TAB ); printf( "10s0, RSTARS ); } /****************************************************************** * procedure derivative * ******************************************************************* 1. Purpose: Derivative computes the derivative of a polynomial 2. Paul Amer, August 1984 3. Inputs: fofx -- array of coefficients of a polynomial degree -- highest degree of all terms in the polynomial 4. Returned Values: deriv_coeffs -- array of the coefficients of the derivative of polynomial whose coefficients were passed in fofx. 5. Procedures/Functions Called: None 6. Procedures/Functions Calling: Newton 7. Local Variables: i -- loop counter representing exponent of a term 8. Global Variables Used (and how modified): None ******************************************************************/ derivative(fofx, degree, deriv_coeffs) float fofx[MAXDEGREE+1], deriv_coeffs[MAXDEGREE+1]; int degree; { int i; /* clear out array of returned values */ for (i=0; i<=MAXDEGREE; i++) deriv_coeffs[i] = 0.0; /* transfer the coefficients of original polynomial into deriv_coeffs */ for (i=0; i<=degree; i++) deriv_coeffs[i] = fofx[i]; /* multiply coefficients by powers */ for (i=1; i<=degree; i++) deriv_coeffs[i] = i * deriv_coeffs[i]; /* move coefficients to power-1 */ for (i=0; i=0; i--) poly_at_pointx = (pointx * poly_at_pointx) + fofx[i]; return(poly_at_pointx); } /****************************************************************** * procedure Newton * ******************************************************************* 1. Purpose: Newton's method improves on the secant method. It uses the tangent instead of the secant so it converges faster. Newton numerically approximates the root of a polynomial using "Newton's method". This method uses the recursive formula: x = x - f(x ) / f'(x ) k k-1 k-1 k-1 where f' represents the instantaneous derivative at a point on the curve and x represents the k th approximation of the root. k 2. Paul Amer, August 1984 3. Inputs: fofx -- array of coefficients of a polynomial lower -- lower endpoint of interval containing the root upper -- upper endpoint of interval containing the root epsilon -- controls solution accuracy by bounding the minimum difference between upper and lower to be tested degree -- highest degree of all terms in the polynomial 4. Returned values: None 5. Procedures/Functions Called: derivative, abs, Horner 6. Procedures/Functions Calling: main 7. Local Variables: last the last (x ) approximation of the root k-2 present the current (x ) approximation of the root k-1 next the next (x ) approximation of the root k newton_root -- root of polynomial computed by "Newton's" method iterations -- number of iterations (k) needed to find the root deriv_coeffs -- coefficients of polynomial's derivative 8. Global Variables Used (and how modified): None ******************************************************************/ Newton(fofx, lower, upper, epsilon, degree) float fofx[ MAXDEGREE + 1 ], lower, upper, epsilon; int degree; { int iterations = 0; float next, present, last, newton_root, deriv_coeffs[MAXDEGREE+1]; /* make initial approximations the endpoints of interval */ last = lower; present = (upper + lower)/2; /* compute derivative of function */ derivative(fofx, degree, deriv_coeffs); /* while approximations > tolerance use Newton's method */ while (abs(present - last) > epsilon) { next = present - (Horner(fofx, present, degree) / Horner(deriv_coeffs, present, degree)); /* prepare for next iteration */ last = present; present = next; /* increment number of times Newton's method is thus far performed */ iterations++; } /* assign and output answer */ newton_root = present; printf("14s0, RSTARS); printf("*%sNEWTONS'S METHOD - root is approximately:%s%8.4f%s*0, TAB, TAB, newton_root, TAB); printf("*%sNEWTON'S METHOD - number of iterations:%s%d%s*0, TAB, TAB, iterations, TAB); printf("14s0, RSTARS); } /************************************************************************* * procedure secant * ************************************************************************** 1. Purpose: Secant numerically approximates the root of a polynomial using the "secant method". This method uses the following recursive formula: x = x - f(x ) / m k k-1 k-1 f(x ) - f(x ) which is the slope of the curve for the k-1 k-2 interval containing the points x and x which are the k-1 st and where m = _________________ k-1 k-2 and k-2 nd approximations of the root. x - x m is the secant. k-1 k-2 2. Paul Amer, August 1984 3. Inputs: fofx -- array of coefficients of a polynomial lower -- lower endpoint of interval containing the root upper -- upper endpoint of interval containing the root epsilon -- controls accuracy of solution by bounding the minimum difference between upper and lower to be tested degree -- highest degree of all terms in the polynomial 4. Returned Values: None 5. Procedures/Functions Called: Horner, abs 6. Procedures/Functions Calling: main 7. Local Variables: last the last (x ) approximation of the root k-2 present the current (x ) approximation of the root k-1 next the next (x ) approximation of the root k iterations -- number of iterations (k) needed to find the root secant_root -- a root of the polynomial as found by "secant" method 8. Global Variables Used (and how modified): None **************************************************************************/ secant(fofx, lower, upper, epsilon, degree) float fofx[MAXDEGREE+1], lower, upper, epsilon; int degree; { float Horner( ); float last, next, present, secant_root; int iterations = 0; /* make initial approximations the endpoints of the interval */ last = lower; present = upper; /* use secant method while approximation is not accurate enough */ while (abs(present - last) > epsilon) /* calculate next approximation of root */ { next = present - (Horner(fofx, present, degree) / ((Horner(fofx, present, degree) - Horner(fofx, last, degree)) / (present - last))); last = present; present = next; iterations++; } /* assign and output answer */ secant_root = present; printf("16s0, RSTARS); printf("*%sSECANT METHOD - root is approximately:%s%8.4f%s*0, TAB, TAB, secant_root, TAB); printf("*%sSECANT METHOD - number of iterations :%s%d%s*0, TAB, TAB, iterations, TAB); printf("16s0, RSTARS); } /****************************************************************** * function sign * ******************************************************************* 1. Purpose: Sign tests whether or not a supplied floating point value is positive (0 is considered positive) 2. Paul Amer, August 1984 3. Inputs: number -- a floating point value to be tested 4. Returned Values: 1 if number is positive (>=0), 0 if negative 5. Procedures/Functions Called: None 6. Procedures/Functions Calling: bisection 7. Local Variables: None 8. Global Variables Used (and how modified): None *********************************************************************/ sign(number) float number; { if (number >= 0.0) return(1); else return(0); }