Problem with ()?():() as ending test in for-loop

Tom Karzes karzes at mfci.UUCP
Fri May 26 04:35:49 AEST 1989


In article <17722 at mimsy.UUCP> chris at mimsy.UUCP (Chris Torek) writes:
}In article <1200 at liszt.kulesat.uucp> vanpetegem at liszt.kulesat.uucp writes:
}}I have a problem with VAX C V2.4-026 under VMS V5.0-2.
}
}}	 for(n = 1;  ((n % 10) ? (k > 2) : (n < 100));  n++)
}
}}... When n becomes equal to 10 the "for-loop" is stopped already.
}}So I think neither the first nor the second test is evaluated and only
}}(n % 10) is taken into account (the first test (k > 2) is only important
}}for n taking values from 50 on).
}
}It sounds as though you have found a bug in the compiler.  The large
}`for' expression should evaluate to either 1 or 0: if (n % 10) is
}nonzero, the result is 1 iff k > 2; otherwise, the result is 1 iff n <
}100.  You should look at the assembly produced by the compiler to see
}whether the compiler simply `forgot' to generate parts of the
}expression---for instance, it may have produced
}
}	n % 10 && k > 2
}
}instead.  If so, rewriting the test as
}
}	((n % 10) != 0 && k > 2) || n < 100
}
}will probably get around the bug.  (Any valid `logical' [true/false]
}e1?e2:e3 expression can always be transformed this way into (e1&&e2)||e3,
}provided e3 has no side effects.)


Unless there's something wrong in the code that wasn't included in the
original message, I agree that this is probably a compiler bug.

However, as for your suggested rewrite and claims about equivalent forms
for logical-valued ?: expressions, I'm afraid you've made a fundamental
error in logic.  The expression (e1 ? e2 : e3) is 0 when e1, e2, and e3
are 1, 0, and 1.  The expression ((e1 && e2) || e3) is 1 for this same set
of values.  I.e., (1 ? 0 : 1) is 0, and ((1 && 0) || 1) is 1.

What you meant to say was that the truth value of (e1 ? e2 : e3) is the
same as the truth value of ((e1 && e2) || ((! e1) && e3)) (although you
may have to be careful about evaluating e1 more than once).  Whether or
not e3 has any side effects is irrelevant (as is the question of whether
or not e2 has any side effects).  If e1 is a problem, you could assign
it to a temporary:  (t = e1, ((t && e2) || ((! t) && e3))).

As for this specific example, consider the case where n is 11 and k is 1.
Then (n % 10) is 1, (k > 2) is 0, and (n < 100) is 1.  So the original
expression is 0.  Your expression is 1.

A correct alternate form would be:

    ((((n % 10) != 0) && (k > 2)) || (((n % 10) == 0) && (n < 100)))
or  (t = n % 10, ((t && (k > 2)) || ((! t) && (n < 100))))

Of course, if there are no assignments to n in the loop other than the
increment code in the for statement, you could safely assume that the
first time n reaches or exceeds 100 is when it is exactly 100, i.e.,
that (! (n < 100)) ==> (n == 100) ==> ((n % 10) == 0).  Then you could
write:

    ((((n % 10) == 0) || (k > 2)) && (n < 100))

or perhaps more efficiently (by avoiding the divide unless it is truly
needed) as:

    ((n < 100) && ((k > 2) || ((n % 10) == 0)))

As I said, this test is NOT equivalent when n is greater than 100, but it
should work for (n <= 100).



More information about the Comp.lang.c mailing list