| Translation A | Translation B |
|---|---|
i := low
goto test
top:
loop body
i +:= 1
test:
if i <= high goto top
code after the loop
|
i := low
if i > high goto bottom
loop invariants can go here
top:
loop body
i +:= 1
test:
if i <= high goto top
a delay slot for loop code!
bottom:
code after the loop
|
The second version's a clear winner according to these criteria:
There are two ways to strength reduce the expression i * i inside a loop, where i is an integer loop index, incremented by one each time.
The first way is to notice that in the progression ... 9 4 1 0 1 4 9 16 25 36 49 64 ..., differences between successive values increase by 2 each time (... -5 -3 -1 1 3 5 7 ...). We'd have code like:
isquared := START * START;
delta := 2 * START + 1;
for i in START .. END loop
-- loop body, using i and isquared
delta +:= 2;
isquared +:= delta;
end loop;
Another idea is to notice (i+1)2 = i2+2i+1, so to get ready for the next iteration just add 2i+1 to the current value of i2.
isquared := START * START;
for i in START .. END loop
-- loop body, using i and isquared
isquared +:= i << 1 + 1;
end loop;
This is an integer arithmetic problem, so all attempts to answer this question by using the reciprocal of c are doomed.
Anyway, to figure this problem out, let's see what a concrete example will yield. Say we have i/3 in a loop,
i -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 i div 3 -3 -2 -2 -2 -1 -1 -1 0 0 0 0 0 1 1 1 2 2 2 3 i mod 3 0 -2 -1 0 -2 -1 0 -2 -1 0 1 2 0 1 2 0 1 2 0
What this tells us is that basically we have results coming in blocks of 3, except for around zero (when there is a block of 5). In other words, every time the mod value hits zero, we're in for a change for the NEXT value of i, EXCEPT when the mod value hits zero because i became 0. As we iterate through the loop we can do modular addition on the mod value, watching for it to become zero. But this is a real pain with the negative mod values, so we should do an initial adjustment so that they always count 0,1,2,0,1,2 even for negative start values. Our loop can be:
idivc := START div c;
imodc := START rem c;
if (START < 0 && imodc != 0) imodc += i;
for i in START .. END loop
-- loop body, using i and idiv c
if (imodc == 0 && i != 0) idivc++;
imodc++;
if (imodc == c) imodc = 0;
end loop;
What about negative denominators?
i -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 i div -3 3 2 2 2 1 1 1 0 0 0 0 0 -1 -1 -1 -2 -2 -2 -3 i mod -3 0 -2 -1 0 -2 -1 0 -2 -1 0 1 2 0 1 2 0 1 2 0
Well the mods work the same way, but the quotients decrease as we increment i. Well, we can precompute the delta, I guess.
idivc := START div c;
imodc := START rem c;
signc := c < 0 ? -1 : 1;
if (START < 0 && imodc != 0) imodc += i;
for i in START .. END loop
-- loop body, using i and idiv c
if (imodc == 0 && i != 0) idivc += signc;
imodc++;
if (imodc == c) imodc = 0;
end loop;
We can factor the invariant to prevent having a branch in a loop:
read(n)
if (n > 0)
for i in 1..100
B[i] := n * i
A[i] := B[i]
else
for i in 1..100
B[i] := n * i