[comp.lang.c] bug re unsigned to signed conversion?

eppstein@garfield.columbia.edu (David Eppstein) (05/24/87)

The following program prints both messages.  I would expect it to print
neither, but at least the second message shouldn't happen.  The
generated C code for the two lines is essentially identical; no doubt
the second line and probably the first should have generated a cast to int.

This is on a Vax running 4.3; I don't know what version C++.
It occurred in a real example and took me much grief to track down.
I'm not convinced the C compiler is correct, which is why I'm also cross
posting to comp.lang.c.
            ------------
#include <stream.h>

int a = -1;
unsigned b = 2;

inline int conv() { return b; }

main()
{
    if (a > b) cout << "(int) -1 > (unsigned) 2 ???\n";
    if (a > conv()) cout << "(int) -1 > (int) (unsigned) 2 ????????\n";
}
-- 
David Eppstein, eppstein@cs.columbia.edu, Columbia U. Computer Science Dept.

donn@utah-cs.UUCP (05/28/87)

David Eppstein says that he expects no output from the following
program:

------------------------------------------------------------------------
#include <stream.h>

int a = -1;
unsigned b = 2;

inline int conv() { return b; }

main()
{
    if (a > b) cout << "(int) -1 > (unsigned) 2 ???\n";
    if (a > conv()) cout << "(int) -1 > (int) (unsigned) 2 ????????\n";
}
------------------------------------------------------------------------

I believe both C and C++ apply the same rules for automatic conversions
in this example.  The first test 'a > b' should produce 1 since 'a' is
converted to unsigned and on a two's complement machine, '(unsigned)
-1' is the largest unsigned integer.  In the second test 'a > conv()',
both operands are signed integers and hence the comparison is a signed
comparison and the result is 0.  C++ 1.2.1 cfront fails to generate a C
cast to coerce the type of the return value of conv() to int; the C++
reference manual (r.8.1) guarantees that an inline function has the
same result as a call to a real function, rather than a call to a
macro, so C++ 1.2.1 is wrong.  (This reminds me of some bugs with
Fortran statement functions and the 4.2 BSD f77 compiler...)

It is probably useful to note here that the 4.3 BSD C compiler is an
'unsigned preserving' compiler in the terms of ANSI C -- unsigned char
and unsigned short promote to unsigned int in implicit conversions.
C++ is explicitly 'value preserving' -- small unsigned types promote to
int (r.6.6, except unsigned short conversion is not treated; in any
case a qualification about unsigned short on machines where sizeof
(unsigned short) == sizeof (int) is needed).  Since C++ 1.2.1 cfront
makes no effort to force its 'value preserving' practice on 'unsigned
preserving' Unix C compilers, it is thus the case that on Unix
machines, C++ is also 'unsigned preserving'.

Donn Seeley    University of Utah CS Dept    donn@cs.utah.edu
40 46' 6"N 111 50' 34"W    (801) 581-5668    utah-cs!donn

PS -- Personal opinion: the argument for 'value preserving' promotion
is that it is less confusing -- it is somehow safer for the novice to
be able to write:

	int i = -1;
	unsigned char uc = 2;
	unsigned int ui = 2;

	if (uc > i)
		printf("ANSI\n");
	if (ui < i)
		printf("ANSI too\n");

I fail to see how this is less confusing...

PPS -- C++ 1.2.1 cfront botches certain constant comparisons between
int and unsigned; for example, the following program prints 'wrong':

------------------------------------------------------------------------
#include <stream.h>

int main()
{
	if ((unsigned) 2 > -1)
		cout << "wrong\n";
	return 1;
}
------------------------------------------------------------------------

If the cast is moved to the right operand of > in the example, C++
1.2.1 cfront simply punts on the constant folding and lets the C
compiler handle it.

msb@sq.UUCP (05/29/87)

Donn Seeley (donn@utah-cs.UUCP) writes:
> ... on a two's complement machine, '(unsigned) -1'
> is the largest unsigned integer.

Actually, (unsigned) -1 should be the largest unsigned int on ANY machine.
	K&R Appendix A ref: section 6.5 on page 184
	ANSI Oct.1 draft ref: section 3.2.1.2 on page 28

What IS true only on a 2's complement machine is that converting a
negative number to unsigned does not change the bit pattern.

Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb
#define	MSB(type)	(~(((unsigned type)-1)>>1))

mar@hpclla.UUCP (06/09/87)

>/ hpclla:comp.lang.c / eppstein@garfield.columbia.edu (David Eppstein) / 12:39 pm  May 24, 1987 /
> The following program prints both messages.  I would expect it to print
>neither, but at least the second message shouldn't happen.  The
>generated C code for the two lines is essentially identical; no doubt
>the second line and probably the first should have generated a cast to int.

>This is on a Vax running 4.3; I don't know what version C++.
>It occurred in a real example and took me much grief to track down.
>I'm not convinced the C compiler is correct, which is why I'm also cross
>posting to comp.lang.c.

>#include <stream.h>

>int a = -1;
>unsigned b = 2;

>inline int conv() { return b; }

>main()
>{
>    if (a > b) cout << "(int) -1 > (unsigned) 2 ???\n";
>    if (a > conv()) cout << "(int) -1 > (int) (unsigned) 2 ????????\n";
>}
-- 
 I compiled this useing the 1.1 version of C++. The C code generated is
 incorrect, you were right in assuming that the C++ compiler should generate
 a cast to int for the inline construct. The C compiler is correct in how
 the code is handled: By ordinary promotion rules, if one operand is unsigned
 then the other is converted to unsigned and the expression result is unsigned.
 Hence, in the expression 'a' is converted to unsigned before the comparison 
 and the result is true: (unsigned) -1 > (unsigned) 2.
 The C code generated for the second expression is identical except for an
 extra set of parens around the return value 'b', which are insignificant in
 this case.