[comp.lang.c] assigning an integer to the negation of a cast of a short to a u_short

lake@alberta.UUCP (Robert Lake) (12/20/88)

Now that I have your attention with the title of the article (which is what
this article is about), let us consider the following program:

	main() {
		int i;
		short j = 1;
		i = -(unsigned short)j;
		printf("i=%d\n", i);
	}

If I run this program on a VAX 11/780 using 4.3 BSD, I obtain -1 as the
answer.  However, if I run this on a SUN using SUN OS 3.5, I obtain 65535
as the answer.  Who is right?  I personally think 65535 should be the
answer, and the 4th line of the program should read:

		i = -(int)((unsigned short)j);

if it is to perform the same function with all C compilers.

In case anyone is wondering why I would want to do anything like this,
you will find the following statement in the file netinet/ip_input.c in
the 4.3 kernel:

		i = -(u_short)ip->ip_len;

which, I think, should really read:

		i = -(int)((u_short)ip->ip_len);

Opinions anyone?
					Rob Lake (alberta!lake)
					University of Alberta

guy@auspex.UUCP (Guy Harris) (12/21/88)

>If I run this program on a VAX 11/780 using 4.3 BSD, I obtain -1 as the
>answer.  However, if I run this on a SUN using SUN OS 3.5, I obtain 65535
>as the answer.

If I run this on a Sun-3 (and probably on other Suns - it's not an
acronym any more, BTW), I get -1.

>Who is right?

Well, let's see:

The expression, when fully parenthesized, is

	-((unsigned short)j)

i.e., the cast is done first, and then the negation.

K&R First Edition says, under 7.2 Unary operators:

	The result of the unary - operator is the negative of its
	operand.  The usual arithmetic conversions are performed.  The
	negative of an unsigned quantity is computed by subtracting its
		    n
	value from 2 , where "n" is the number of bits in an "int".

Under 6.6 Arithmetic conversions, it says:

	First, any operands of type "char" or "short" are converted to
	"int", and any of type "float" are converted to "double".
	(...stuff about "double" and "long", which doesn't matter here...)
	Otherwise, if either operand is "unsigned", the other is
	converted to "unsigned" and that is the type of the result.

Unfortunately, "unsigned short" didn't exist in K&R First Edition C, so
the question is "what do the 'usual arithmetic conversions' do to an
'unsigned short' in more modern C?"  If it gets widened to "int", the
right answer is -1; if it gets widened to "unsigned int", the right
answer is 2^32 - 1 on a machine with 32-bit "int"s.

K&R Second Edition, based on some ANSI C draft, says:

	A7.4.5 Unary Minus Operator

	...The integral promotions are performed....

and

	A6.1 Integral Promotion

	   A character, a short integer, or an integer bit-field, all
	either unsigned or not, may be used in an expression wherever an
	integer may be used.  If an "int" can represent all the values
	of the original type, then the value is converted to "int";
	otherwise the value is converted to "unsigned int".

Since, if "int" has more bits than "unsigned short", any "unsigned
short" value can fit into an "int" (that is the case on both the VAX and
on all Suns), the "unsigned short" value of "(unsigned short)j" is
converted to "int" before it is negated; thus, the right answer in dpANS
C is -1. 

In "modern but not dpANS" C, though, I'm not sure.  At least one draft
of the ANSI C Rationale I saw spoke of a change from
"unsignedness-preserving" to "value-preserving" conversion rules, the
latter being pretty much the rules described under "Integral Promotion". 
As such, the old rules may have been "unsignedness-preserving"; I
presume this means that the "usual arithmetic conversions" cause the
"unsigned short" to be promoted to "unsigned int".  What this means is
that an "unsigned short" value of "1" is promoted to an "unsigned int"
value of "1"; this is negated, yielding 2^32-1.  This is then assigned
to an "int", yielding -1.... 

So it appears 65535 isn't right in dpANS C nor, probably, in "modern but
not dpANS" C.