Do-While Loops

Suppose you want the user to enter three integers for sides of a right triangle. If they do not make a right triangle, say so and make the user try again.

One way to look at the while statement rubric is:

set data for condition
while (condition) {
   accomplish something
   set data for condition
}

As we have pointed out before this involves setting data in two places. With the triangle problem, three pieces for data need to be entered, and the condition to test is fairly simple. (In any case the condition could be calculated in a function.)

A do-while loop will help here. It tests the condition at the end of the loop, so there is no need to gather data before the loop:

int a, b, c;
do {
    Console.WriteLine("Think of integer sides for a right triangle.");
    a = UI.PromptInt("Enter integer leg: ");
    b = UI.PromptInt("Enter another integer leg: ");
    c = UI.PromptInt("Enter integer hypotenuse: ");
    if (a*a + b*b != c*c) {
        Console.WriteLine("Not a right triangle: Try again!");
    }
} while (a*a + b*b != c*c);

The general form of a do-while statement is

do {
statement(s)
} while ( continuationCondition );

Here the block of statement(s) is always executed at least once, but it continues to be executed in a loop only so long as the condition tested after the loop body is true.

Note

A do-while loop is the one place where you do want a semicolon right after a condition, unlike the places mentioned in Dangerous Semicolon. At least if you omit it here you are likely to get a compiler error rather than a difficult logical bug.

A do-while loop, like the example above, can accomplish exactly the same thing as the while loop rubric at the beginning of this section. It has the general form:

do {
   set data for condition
   if (condition) {
       accomplish something
   }
} while (condition);

It only sets the data to be tested once. (The trade-off is that the condition is tested twice.)

In the example above note that the declaration of a, b, and c is before the do-while loop. You can try moving the declaration inside the braces for the loop body, and see the compiler error that you get!

Note

Recall the variables declared inside a braces-delimited block have scope local to that block. The condition at the end of the loop is outside that scope. Hence the declaration of variables that you want in the final test or for later use after the loop must be declared before the do-while loop.

Loan Table Exercise

Loans are common with a specified interest rate and with a fixed periodic payment. Interest is charged at a fixed rate on the amount left in the loan after the last periodic payment (or start of the loan for the first payment).

For example, if an initial $100 loan is made with 10% interest per pay period, and a regular $20 payment each pay period: At the time of the first payment interest of $100*.10 = $10 is accrued, so the total owed is $110. Right after the payment of $20, $110 - $20 = $90 remains. That $90 gains interest of $90*.10 = $9 up to the next payment, when $90 + $9 = $99 is owed. After the regular payment of $20, $99 - $20 = $79 is left, and so on. When a payment of at most $20 brings the amount owed to 0, the loan is done.

We can make a table showing

  • Payment number (starting from 1)

  • The principal amount after the previous payment (or the beginning of the loan for the first payment)

  • The interest on that principal up until the next periodic payment

  • The payment made as a result.

Continuing the example above, the whole table would look like:

Number Principal   Interest    Payment
     1    100.00      10.00      20.00
     2     90.00       9.00      20.00
     3     79.00       7.90      20.00
     4     66.90       6.69      20.00
     5     53.59       5.36      20.00
     6     38.95       3.90      20.00
     7     22.85       2.29      20.00
     8      5.14       0.51       5.65

In the final line, the principal plus interest equal the payment, finishing off the loan.

Similarly, with a $1000.00 starting loan, 5% interest per pay period, and $196 payments due, we would get

Number Principal   Interest    Payment
     1   1000.00      50.00     196.00
     2    854.00      42.70     196.00
     3    700.70      35.04     196.00
     4    539.74      26.99     196.00
     5    370.73      18.54     196.00
     6    193.27       9.66     196.00
     7      6.93       0.35       7.28

If a $46 payment were specified, the principal would not decrease from the initial amount, and the loan would never be paid off.

There are a couple of wrinkles here: double values do not hold decimal values exactly. The decimal type does hold decimal numbers exactly (and in an enormous range, see Numeric Types and Limits) and hence are beter for monetary calculations. Decimal literals end with m, like 34.56m for exactly 34.56.

Though decimals are exact, money only has two decimal places. We make the assumption that interest will always be calculated as current principal*rate, rounded to two decimal places: Math.Round(principal*rate, 2).

Write loan_calc.cs`, completing LoanTable and write a Main testing program:

/// Print a loan table, showing payment number, principal at the
/// beginning of the payment period, interest over the period, and
/// payment at the end of the period.
/// The principal is the initial amount of the loan.
/// The rate is fraction representing the rate of interest per PAYMENT.
/// The periodic regular payment is also specified.
/// If the payment is insufficient, merely print "payment too low".
public static void LoanTable(decimal principal, decimal rate,
                             decimal payment)

Note that the rate, too, is a decimal, even though it does not represent money. That is important, because arithmetic with a decimal and a double is forbidden: A double would have to be explicitly cast to a decimal. Insisting on decimal parameter simplifies the function code.

This exercise is much more sophisticated than the Savings Exercise, so it is placed in this section, much later in the chapter. Use what ever form of loop makes the most sense to you.