greg@utcsri.UUCP (Gregory Smith) (02/20/86)
In article <1251@watmath.UUCP> rbutterworth@watmath.UUCP (Ray Butterworth) writes: >I wonder how many tens of thousands of dollars of time and resources >have been wasted recently by all of these complaints and discussions >about what the % operator should do. > >The real problem of course isn't that the % operator was definded >correctly or incorrectly, the problem is that it is intentionally >poorly defined in C. The expression -5%2 will be positive on some >implementations, and negative on others, but in both cases the answer >will be "correct". The nail has been finally hit on the head. I see C as about the closest thing we have to a 'portable' assembler, for the following reasons ( among others ): - The language has 'machine' operators like >>,<<, &,|,~ and pointer operations, available to the user. - C does not 'hide' the fact that a computer memory consists of a linear homogenous address space (except maybe 8086 >:-) ) The C programmer normally has to be aware of this. In most other high-level languages, this is not the case. - C does not hide the fact that machines are different from each other. It tries to, but it does not. The programmer needs to be very much aware of the nature of these differences to be able to write portable code. This is not the case in most other HLLs. Now the point. If C is to be an 'assembler' or 'high-level-assembler', it should be able to produce very efficient code, to eliminate one of the prime reasons for using 'real' assembler. For the produced code to be very efficient, it should be as near as possible to the bare instructions required to do what needs doing. Now it is a hard fact that machines have hardware 'mod' and 'div' operators, and different machines produce different results for these. I do not think it would be a good idea to standardize the results of / and %, because then machines which did not produce the 'correct' results would have problems. The compiler would have to emit clumsy sign- checking code, or call a routine to do the operation. This is not consistent with the 'efficiency' objective. I believe the designers of C were thinking exactly along these lines. More evidence: char to int conversion may or may not be sign-extended. The PDP-11 makes it dashed clumsy to move a char to an int *without* sign-extend, thus PDP-11 C sign-extends. If the machine cannot do this extension easily, the no-extend operation should take place. More: some types ( unsigned long, unsigned char ) may not be allowed. If you need a certain type of division/mod/whatever not guaranteed, do one of the following: (1) write a non-portable version, if you know it will never need to be ported. (2) use #defines: #define div(a,b) ((a)/(b)) on one machine, #define div(a,b) ((a)<0?-(-(a)/(b)):(a)/(b)) on another, etc (3) Don't use C :-). Note that option 2 corresponds to making the compiler emit code for machines with the 'wrong' divide operation. Except this way you only get it when you need it. Macros like this could even be provided in system-dependant header files, to provide commonly used 'guaranteed' operations. Like toupper(c) and things like that. Greg Smith University of Toronto --------------------------------------------------------------------- These opinions are mine and are not necessarily those of anybody else nor are they necessarily very original! ( =-> )