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.