[net.lang.c] Problem in type conversion

obrien@randvax.ARPA (Michael O'Brien) (12/31/83)

We think there may be a serious problem with type conversion.  Consider the
following program:

int i = 6, j = 6;
main() {
	i *= .5;
	j = j * .5;
	printf ("%d, %d\n", i, j);
}

This program will show i = 0 and j = 3.  The problem is that the ".5" in
the first line is converted to an integer before the multiplication takes
place, and in the second line, the multiplication is done in double precision
before the integer conversion occurs.

This seems to violate Kernighan and Ritchie, which states that

	E1 op= E2

may be regarded as identical to

	E1 = E1 op (E2);

when E1 and E2 are of arithmetic type, except that E1 is evaluated only
once in the first case.  That single difference does not seem to imply such
an egregious difference in type conversion.

We see this both in 4.1 and 4.2 BSD PCC, and in the Ritchie compiler for V7
on the PDP-11.  We don't have System III or System V compilers handy.

Any comments?

keesan@bbncca.ARPA (Morris Keesan) (01/03/84)

-------------------------------
    The problem with ( E1 op= E2 ) generating a different result than
( E1 = E1 op (E2) ) may or may not be a compiler problem.  The root of the
problem seems, as usual, to be in the language definition.  Section 7.14
of the C Reference Manual (p. 191 of K&R) gives a list of all the assignment
operators, and then says (capitals mine):

	In the simple assignment with =, the value of the expression
    replaces that of the object referred to by the lvalue.  If both      
    operands have arithmetic type, the right operand is converted to
    the type of the left preparatory to the assignment.
	The behavior of an expression of the form E1 op= E2 MAY BE
    INFERRED BY TAKING IT AS equivalent to E1 = E1 op (E2); . . .

It is unclear here whether the second sentence, referring to type conversions,
applies only to the simple assignment with =, or to all the assignment
operators.  It is equally unclear whether the next sentence is an exact
definition of what the op= operators do, or a definition by example and
hand-waving.  Simply reading the manual would seem to indicate that the
conversion of the right operand to the type of the left should only occur for
the simple case, but the behavior of the compilers would seem to indicate a
different intent.
    What is clear is that there is a serious problem here, as you say, and that
the language definition should be revised in the direction of clarity.  I'm not
sure whether this should mean changing the language to behave in the
intuitively correct fashion, or changing the manual to reflect the actual
behavior of the existing compilers.  It's the old problem of improvement vs.
compatibility.
-- 
					Morris M. Keesan
					{decvax,linus,wjh12}!bbncca!keesan
					keesan @ BBN-UNIX.ARPA

roger@fluke.UUCP (Roger Ferrel) (01/04/84)

What we see here with the:

	inttype *= 0.5;
		vs
	inttype = inttype * 0.5;

is a weekness of most C compilers. It seems none of them handles float
operations very well when there are mixed types.  Of course the above is
a poor example since normally more efficent code would be generated by
using:

	intype >>= 1;  /* how is this for getting off the subject? */

Whenever coding in C I avoid floats and doubles as much as 
possible.  When using them in an expression I usually make sure to
explicitly cast everything to doubles and I tend to stay away from
the "fancy" assignment operators in mixed type expressions.

Thus for the first example I would code:

	inttype = (int) ( ((double) inttype) * ((double) 0.5));

I know it looks messy but many C compilers need the hints to generate
proper code.

Rules to follow:

	1) In an expression with mixed type explicitly cast operands to
	   biggest type in the expression since this will be the size
	   of the result. (Especially if the largest type is a double.)
	   Types largest to smallest are:

		double, long, int, short, char

	   Many C compilers convert smaller types to int before doing
	   anything with them in an expression.

	   Floats (single precision) should always be cast to double
	   since C supports double operations but not single.  To
	   find out how good your compiler is with mixed operands look
	   at the code produced for:

		 float f;
		 int   i;
		 f *= i;  /* f and i should be cast to doubles and the
			   * result reduced to single precision float
			   */

	2) Explicitly cast the result of an expression to the same type as
	   the target variable when the target is of smaller type then
	   the expression.  (This is being very conservative.)

	3) If the expression will be of smaller type then the variable
	   the result is assigned to cast each operand of the
	   expression to the type of the variable you are doing the
	   assignment to.  This will avoid unplanned trucation errors.
	   Example:

		int i;
		double d;

		d = (double) i / (double) 17;

Following these rules avoids the implicit cast bugs in many C
compilers.  In practice most compilers do a good job of casting as long
as you stick with the various types of integers.  When you start using
floats and doubles that is the time to follow the above rules closely.
-- 

    ------
    Roger L. Ferrel       (206) 356-5056
    John Fluke Mfg. Co.
    P.O. Box C9090  Everett WA  98206  
    {uw-beaver,decvax!microsof,ucbvax!lbl-csam,allegra,ssc-vax}!fluke!roger

myunive@nsc.UUCP (Jay Zelitzky) (01/05/84)

........
	The accepted definition of C as I have seen in all versions of the
C compiler is that in assignment ops, as in assignments; the right side
is converted to the type of the left side before the op is done.  This makes
sense since assignment ops are treated the same as regular assignments.  This
is what the C reference manual means to say.
				Jay
				hplabs!menlo70!nsc!myunive

addw@root44.UUCP (01/19/84)

<<<<<To be eaten by mailers>>>>>
Roger L. Ferrel said:

>> What we see here with the:
>> 
>> 	inttype *= 0.5;
>> 		vs
>> 	inttype = inttype * 0.5;
>> 
>> is a weekness of most C compilers. It seems none of them handles float
>> operations very well when there are mixed types.  Of course the above is
>> a poor example since normally more efficent code would be generated by
>> using:
>> 
>> 	intype >>= 1;  /* how is this for getting off the subject? */

Unfortunately this is only guaranteed to work if:
1)	inttype is unsigned -plainly not the intention.
2)	inttype is positive, if inttype is negative it is undefined
what is put into the top bit; ie your negative number may become positive.
(A '1' will be inserted on a PDP11, but I assume that it is intended to write
portable programs. I don't know what other machines will do.)

There is little point in being efficient but wrong.
The only real alternative is:
	inttype /= 1;

	Alain Williams
	Root Computers Ltd, London.
	{ENGLAND}!ukc!root44!addw

PS, In England we spell the word that means 'lack of strength' as weakness,
& efficent as efficient.