J
John Kelly
It may not be pleasing or sneaky, but it does work under C90.
That's a start. But I want C99 too.
6.3.1.3 Signed and unsigned integers
3 Otherwise, the new type is signed and the value cannot be
represented in it; either the result is implementation-defined
or an implementation-defined signal is raised.
So when the value assigned to new, which is signed (ptrdiff_t), cannot
be represented, a signal may be raised. Uh-oh, the UB boogeyman!
Let's look again,
for (max = 32767; (new = 2ul * max + 1) > max
and think about what 6.3.1.8 says
... integer promotions are performed on both operands. Then the
following rules are applied to the promoted operands:
(a) If both operands have the same type, then no further conversion is needed.
Not true. 2ul is unsigned, max (ptrdiff_t) is signed.
(b) Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.
Not true. Same as above.
(c) Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.
Possibly false. Rank of (ptrdiff_t) may be greater than 2ul.
(d) Otherwise, if the type of the operand with signed integer type can represent
all of the values of the type of the operand with unsigned integer type, then
the operand with unsigned integer type is converted to the type of the
operand with signed integer type.
Possibly false. Rank of (ptrdiff_t) may be less than 2ul.
(e) Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
The (e) catch-all has the same effect as (c). 2ul is already unsigned
and max is converted to unsigned. So with (c) or (e), both operands are
sure to become unsigned. Thus the "l" in "2ul" is redundant. It can be
reduced to 2u. That is enough to ensure (c) or (e).
OK. But the UB boogeyman is still lurking in:
(new = 2u * max + 1)
because "new" is signed (ptrdiff_t), and we're back to 6.3.1.3
Otherwise, the new type is signed and the value cannot be represented
in it ... an implementation-defined signal is raised.
Oops. Hmmm. We could assign to a different variable which is unsigned,
but we have no way of knowing what rank to choose for it, since we don't
know the rank of (ptrdiff_t). Chicken and egg problem.
If we could just pretend that (ptrdiff_t) is unsigned, the boogeyman
would go away. Hmmm. I never tried casting the lhs of an assignment
before. Wonder if it works?
Wait for it ...