jacob navia wrote:
> I posted this to comp.std.c, but may be of interest here too:
>
> Consider this:
>
> extern void abort(void);
> int main (void)
> {
> unsigned long long xx;
> unsigned long long *x = (unsigned long long *) &xx;
What is the cast for? (Hint: What is the type of
the expression `&xx'?)
> *x = -3;
> *x = *x * *x;
> if (*x != 9)
> abort ();
> return(0);
> }
>
> lcc-win interprets
> *x = -3;
> as
> *x = 4294967293;
That cannot possibly be correct. ULLONG_MAX is at
least 18446744073709551615, so ULLONG_MAX-2 (the required
result) is at least 18446744073709551614.
> since x points to an UNSIGNED long long.
> I cast the 32 bit integer -3 into an unsigned integer
> then I cast the result to an unsigned long long.
That would be correct iff ULLONG_MAX==ULONG_MAX.
Imagine converting a signed char to an unsigned long
by the analogous route. Let's assume an 8-bit char, a
16-bit int, and a 32-bit long (and because there's no
way to write a literal of type char, I'll need to use
a variable instead):
signed char sc = -3;
unsigned long ul = sc;
The procedure you've outlined would convert sc to an
unsigned int, getting 65533u, and then convert that value
to unsigned long, yielding 65533ul. Yet the correct
result is ULONG_MAX-2 == 4294967293. The intermediate
conversion has lost sign information that affects the
ultimate result.
> Apparently gcc disagrees.
>
> Am I doing something wrong somewhere?
>
> I should first cast into a long long THEN into an unsigned
> long long?
You should convert the signed int to unsigned long long
by adding or subtracting ULLONG_MAX+1 the appropriate number
of times: in this case, by adding it once.
--