[comp.lang.misc] "for" loops

chris@mimsy.UUCP (Chris Torek) (01/30/89)

[followups redirected to comp.lang.misc]
In article <8515@lanl.gov> jlg@lanl.gov (Jim Giles) writes:
>[the floating point DO-loop construct] is considered a mistake because
>the standard doesn't make any constraints on the accuracy of floating-
>point arithmetic.  So the trip count on a loop with floating-point
>limits is not well-defined.

(True in both C and FORTRAN.)

>The C for-loop is even worse in this regard - the trip count is not
>precomputed.

That this is worse is mere opinion (or perhaps contentiousness).

>Consider the following:
>
>      for (x=0.0; x<=1.0, x+=0.1) {...}

Or consider:

	tripcount = (int)((1.0-0.0)/0.1) + 1;
		/* or should this be rounded rather than truncated? */
		/* (I have no F77 reference handy, sorry) */
	for (x = 0.0; --tripcount >= 0; x += 0.1) ...

which (in theory) does what the F77 construct

	DO label X = 0.0, 1.0, 0.1

does.  But suppose the task is to find the accumulated error?

	for (i = 0, x = 0.0; x <= 1.0; i++, x += 0.1)
		/* void */;
	printf("[0.0,1.0] count took %d steps; x-1.0=%g\n", i, x - 1.0);

which in F77 can be written (I will use `WHILE' here for conciseness
and to avoid those blasted statement numbers)

	X = 0.0
	I = 0
	WHILE (X .LE. 1.0) DO
	    X = X + 0.1
	    I = I + 1
	ENDWHILE
		! (I forget whether PRINT * puts spaces around numbers)
	PRINT *, '[0.0,1.0] count took', I, 'steps; x-1.0=', X-1.0

Not really that different after all (although the FORTRAN version is
in ugly uppercase :-) ).

>The value of x for each trip through the loop is a sum of 0.1+ ... +0.1,
>a value which suffers round-off or truncation on each successive add.  
>If the four operations in the computation of the Fortran trip count
>lead to ambiguous results, the 11 adds in the above are even worse.

Any programmer worth his pay is aware of the method by which the trip
count is determined, and will write the appropriate code in the appropriate
situation.  It is true that situations demanding floating point loop
counters are rare.

In any case, the generalised `for' loop construct in C is very
worthwhile, although a specialised precomputed-count loop construct (a
la most other Algol/FORTRANnish languages) might be nice.  C's `for'
fits a number of situations well, such as

	for (p = head; p != NULL; p = p->next)

for simple list traversal, or

	for (p = head.next; p != &head; p = p->next)

for circular queues, or

	for (x = init; fn1(x) < fn2(x); x = fn3(x))

for essentially arbitrary stepping around a graph of two functions
looking for a crossover.  For loops with a fixed iteration limit,
a construct like

	for i in [1..N) do

is often better, since it is clearer and allows the compiler to use
shadowing or downcounts or whatnot without requiring it to determine
that the limit is in fact fixed.  It is sometimes worthwhile in C to
recode something like

	for (i = 1; i < f(); i++)	/* where f() is a pure function */

as

	for (j = f(), i = 1; --j > 0; i++)

or (clearer but sometimes slower)

	for (i = 1, j = f(); i < j; i++)

and this sort of nonsense is really best left to the compiler.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

jlg@lanl.gov (Jim Giles) (01/31/89)

From article <15692@mimsy.UUCP>, by chris@mimsy.UUCP (Chris Torek):
> [...]
>>The C for-loop is even worse in this regard - the trip count is not
>>precomputed.
> 
> That this is worse is mere opinion (or perhaps contentiousness).

It's worse in the sense that the loop without a clear trip count calculation
may not terminate at all!
 
Consider the following:

      for (x=0.0; x<=1.0, x+=0.0000001) {...}

The intent is for 10 million steps.  But if IEEE single is used, the
increment becomes a no-op at some point.  An explicit trip count calculation
would at least not exhibit this problem.  Rounding during the divide step
would probably select the wrong trip count - but it wouuld still select
a finite one.

(before you shout that a 10 million trip loop is an unrealistic example,
I should point out that such things are standard fare around here.) 

> [...]
> which in F77 can be written (I will use `WHILE' here for conciseness
> and to avoid those blasted statement numbers)

Why not use the Fortran 8x style DO - END DO pair: no statement labels
there :-).> 

> [...]
> Any programmer worth his pay is aware of the method by which the trip
> count is determined, and will write the appropriate code in the appropriate
> situation.  It is true that situations demanding floating point loop
> counters are rare.

Here's where we part company in a big way. I oppose ANY language construct
that requires the programmer to know internals about how the construct
is performed.  Both Fortran and C (and most everything else for that matter)
already have too many such constructs.  It is probable that a compiled
language CAN'T be written with SOME such constructs - but let's keep
them at a minimum.

For example, it's bad enough that in the above example you really have to 
write:

      for (i=0, i<10000000, i++) { x=i*0.0000001; ...}

Now, the relative error in the value x during execution remains fairly
constant (rather than accumulating as in the incremented version).  Many
Fortran compilers actually implement DO loops with real limits in the
above manner.  The standard should really specify that this should be
done so the user needn't worry about it.

J. Giles
Los Alamos

chris@mimsy.UUCP (Chris Torek) (02/19/89)

In article <8618@lanl.gov> jlg@lanl.gov (Jim Giles) writes:
>Here's where we part company in a big way. I oppose ANY language construct
>that requires the programmer to know internals about how the construct
>is performed.

I will agree that `dark corners' or ambiguities are problematical (and
C is full of them, so that programmers must exercise restraint; alas,
many do not).  But the number of iterations of a loop had better not be
a `dark corner'!  If you cannot predict the trip count given the limits
and increments, you have no business writing that loop in the first
place!

>.... Many Fortran compilers actually implement DO loops with real limits
>in the above manner [avoiding accumulated roundoff errors].  The standard
>should really specify that this should be done so the user needn't worry
>about it.

And if the standard says it, or just as importantly if it does *not*,
programmers using the language should be aware of it!  If the standard
says that DO-loop trip counts depend on the phase of the moon, the
programmer should be prepared to look out the window as necessary.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris