next up previous
Next: About this document ... Up: The Code Previous: C++ code

Explanation of New Concepts

Here we explain some new features in the code nr.cc.




#define N 100    // Maximum number of iterations

This is a common way to define a constant that could be changed some day. Notice that the maximum number of iterations N is used twice in the code. It is good practice to refer to it as a symbol and define the value of the symbol only once. Then if we change its value, we need to make only one change and don't have to search the code. The #define statements should come near the top of the code where they are easy to find. The symbol N is technically called a ``macro''. The compiler preprocessor automatically replaces every instance of the macro with its value before turning the code over to the compiler. So N is not a variable, has no specific type, and is not allocated storage.




  for(i = 0; i < N; i = i + 1){
    ...
  }

This is a for loop. It specifies that the statements between the braces should be executed repeatedly as long as i < N, starting with i = 0 and increasing i by one when the last statement in the braces is reached.

We use a for loop here to impose a maximum number of Newton Raphson iterations, and prevent the code from running on endlessly.

The generic pattern is

  for(starting-statement; logical-expression; incrementing-statement){
    statement block
  }
Note that the logical expression is tested before entering the block of statements. So in the above example, if it happened that N == 0 the condition i < 0 would be false and the statement block would be skipped altogether.

Because incrementing a counter by one unit is done so often, C++ has a shorthand, i++ in place of i = i + 1; you will often see a for loop written this way:

  for(i = 0; i < N; i++){
    ...
  }
It is also written ++i. In this context it means the same thing.

We could accomplish the same thing with a while loop. Here is the logically equivalent pattern;

  i = 0;
  while(i < N){
    ...
    i++;
  }
Notice that here we have to take care to place the initialization and incrementing statements in the correct place. And it takes three statements to do the job of one for statement. For those reasons the for loop is preferable here.

The generic form is

  while(logical-expression){
    statement-block
  }
The while loop repeats the statements inside the statement block as long as the logical expression is true. It is up to the programmer to assure that there is an escape from the loop, so it doesn't run on indefinitely - another reason to be cautious about using a while loop where a for loop will do.

The do ... while structure is much less used. The general syntax is

  do{
    statement-block;
  } while(logical-expression);
This loop structure is very similar to the while loop structure, except that the logical expression is checked only after executing the statement block at least once. (With the while loop the statement block is not executed at all if the logical expression is false.) Then in both cases the block is executed repeatedly as long as the logical expression remains true.




    if(abs(p-pnew) < tol){
      ...
    }

This test is called the stopping condition for the method. We stop when the absolute value of the change in the estimated root is less than the specified tolerance. We want the absolute value of the change, since it could be positive or negative.




      return 0;

This statement forces an exit from the main program. The value 0 is the exit code. It is common practice to use an exit code of 0 for a normal completion and a nonzero code for an abnormal condition. Notice that at the bottom of the code we give an exit code of 1 to signal that the method did not converge. You can check the exit code of your program in the shell tcsh using the Unix command
   echo $status
immediately after your program exits. In the bash or sh shell the command is echo $?.

Another common way to exit the main program is to call the exit function as in

      exit(0);
where the number in parentheses is the desired exit code.

The break statement provides another way out of a loop. We could have written

  for(i = 0; i < N; i = i + 1){
    ...  
    if(abs(p-pnew) < tol)break;
  }
The break statement immediately quits the statement block controlled by a for, while or do ... while loop, and proceeds with the next statement after the closing brace.

The logical problem in this case is that we reach the same point in the code if the process doesn't converge. So we need to check the value of i to be sure that didn't happen. For example, we could end it this way:

  for(i = 0; i < N; i = i + 1){
    ...  
    if(abs(p-pnew) < tol)break;
  }
  if(i >= N){
      cerr << "Failed to converge after " << N << " iterations.\n";
      return 1;
  }
  cout << "Root is " << pnew << " to within " 
       << tol << "\n";
  return 0;

For completeness we mention the continue statement. It provides another way to skip around in a loop. While break skips the rest of the statement block and quits the loop, the continue statement skips the rest of the statement block and proceeds with the next iteration. Here is an example of its use in the same code:

  for(i = 0; i < N; i = i + 1){
    ...  
    if(abs(p-pnew) > tol)continue;
    cout << "Root is " << pnew << " to within " 
    << tol << "\n";
    return 0;
  }
  cerr << "Failed to converge after " << N << " iterations.\n";
  return 1;
Notice that the inequality is reversed so the loop continues as long as the shift in the root is larger than the tolerance and i < N. I find the original coding style clearer, however, since the code handling the special case is set off clearly with braces and indentation. The continue statement is more appropriate when the standard nesting of statement blocks is awkward or when continuation is the unlikely alternative, which is not the case here.


  cerr << "Failed to converge after " << N << " iterations.\n";

The iostream object cerr works just like cout but sends the output to the stderr device instead of stdout. By default the stderr device, like the stdout device, is the console. The main reason for using it for error messages is that if a user is redirecting standard output to a file or pipe, the error message would still appear on the screen rather than disappearing into the file or pipe and possibly being ignored.


next up previous
Next: About this document ... Up: The Code Previous: C++ code
Carleton DeTar 2009-09-16