rbutterworth@watmath.waterloo.edu (Ray Butterworth) (02/15/88)
I just got hit by yet another way not to write a loop: auto size_t index; index = sizeof(array) / sizeof(array[0]); while (--index >= 0) blah blah blah This looks wonderfully portable and works fine on the BSD 4.3 compiler (and probably most others). But on an ANSI compiler, (size_t) will be an unsigned integer and the loop will go forever. Lint will notice this problem, but only with the ANSI compiler when it is probably too late.
crowl@cs.rochester.edu (Lawrence Crowl) (02/17/88)
In article <16941@watmath.waterloo.edu> rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes: ]I just got hit by yet another way not to write a loop: ] ] auto size_t index; ] ] index = sizeof(array) / sizeof(array[0]); ] ] while (--index >= 0) ] blah blah blah ] ]This looks wonderfully portable and works fine on the BSD 4.3 compiler ](and probably most others). ] ]But on an ANSI compiler, (size_t) will be an unsigned integer ]and the loop will go forever. ] ]Lint will notice this problem, but only with the ANSI compiler ]when it is probably too late. Fortunately, there is an easy fix. while ( index-- > 0 ) -- Lawrence Crowl 716-275-9499 University of Rochester crowl@cs.rochester.edu Computer Science Department ...!{allegra,decvax,rutgers}!rochester!crowl Rochester, New York, 14627
decot@hpisod2.HP.COM (Dave Decot) (02/17/88)
> In article <1988Feb11.200149.25172@sq.uucp> msb@sq.UUCP (Mark Brader) writes: > > Floating point numbers should never be used for counting. > > Never say never. (Well, hardly ever say never.) At least one > widely-used implementation of Snobol4 successfully uses a floating > point register to count the number of statements executed, relying on > floating point overflow being detected by hardware so that the program > may be aborted when the user-specified statement count limit is > reached. We have a misunderstanding of the word "should", apparently. Dave Decot hpda!decot
rbutterworth@watmath.waterloo.edu (Ray Butterworth) (02/18/88)
In article <6848@sol.ARPA>, crowl@cs.rochester.edu (Lawrence Crowl) writes: > In article <16941@watmath.waterloo.edu> > rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes: > > I just got hit by yet another way not to write a loop: > > auto size_t index; > > index = sizeof(array) / sizeof(array[0]); > > while (--index >= 0) > > This looks wonderfully portable and works fine on the BSD 4.3 compiler > > (and probably most others). > > But on an ANSI compiler, (size_t) will be an unsigned integer > > and the loop will go forever. > Fortunately, there is an easy fix. > while ( index-- > 0 ) Thanks (sincere, not sarcastic) to those that have sent me this suggestion. I was aware that this is a much better way of doing it. The problem I was trying to point out is that much existing code wasn't written with perfect style, and this is yet another way in which otherwise correct code will break when it is ported to an ANSI compiler (as I found out the hard way).
dhesi@bsu-cs.UUCP (Rahul Dhesi) (02/19/88)
>> > Floating point numbers should never be used for counting. >> Never say never....At least one >> widely-used implementation of Snobol4 successfully uses a floating >> point register to count the number of statements executed... >We have a misunderstanding of the word "should", apparently. There might be confusion between "should not" and "should never". Floating point numbers should not be used for counting except when such use gains us a remarkable degree of badly-needed efficiency. In the Snobol (actually SPITBOL) example, the choice was between the equivalents of the following: count -=1; /* integer arithmetic with software test */ if (count == 0) abort_program; and count +=x; /* fast hardware floating add */ /* actually AUR SCNT,SINC on IBM 360 */ Note that the language required one or the other of the above to be done before every statement executed. -- Rahul Dhesi UUCP: <backbones>!{iuvax,pur-ee,uunet}!bsu-cs!dhesi
gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/19/88)
In article <16982@watmath.waterloo.edu> rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes:
-> > auto size_t index;
-> > index = sizeof(array) / sizeof(array[0]);
-> > while (--index >= 0)
-The problem I was trying to point out is that much existing
-code wasn't written with perfect style, and this is yet another
-way in which otherwise correct code will break when it is ported
-to an ANSI compiler (as I found out the hard way).
The fact that the result of sizeof has an unsigned integral type is
not new with ANSI C. UNIX C has been this way since around 1980 at
least. You're getting confused since Berkeley didn't pick up this
change when they stole the pre-USG 3.0 C compiler (circa 1979) that
is included in their distribution.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/19/88)
In article <2150@bsu-cs.UUCP> dhesi@bsu-cs.UUCP (Rahul Dhesi) writes: >Floating point numbers should not be used for counting except when such >use gains us a remarkable degree of badly-needed efficiency. In the >Snobol (actually SPITBOL) example, the choice was between the >equivalents of the following: > count -=1; /* integer arithmetic with software test */ > if (count == 0) > abort_program; >and > count +=x; /* fast hardware floating add */ > /* actually AUR SCNT,SINC on IBM 360 */ >Note that the language required one or the other of the above to >be done before every statement executed. So, the Snobol implementor made a stupid decision, then. I don't have an IBM 360 at hand, but I do have a Gould PowerNode 9080, which has a remarkably similar architecture (including floating-point format) to the IBM 360's. I jury-rigged a crt0.o to enable floating- point exception trapping (normally it has to be disabled, to avoid trapping on integer overflow), and ran a large number of iterations (that did nothing but iterate) using floating-point trapping to terminate the loop, then an identical number of iterations testing an integer counter. The floating-point version burned 4.3 seconds of CPU time while the integer counter version's overhead was only 0.5 seconds. Note that the time required to perform the integer counter test against zero is utterly insignificant by comparison with whatever work the "statement execution" would involve, and it is portable besides. This looks like another instance of "misplaced efficiency concerns".
cdl@mplvax.nosc.MIL (Carl Lowenstein) (02/19/88)
In article <2150@bsu-cs.UUCP> dhesi@bsu-cs.UUCP (Rahul Dhesi) writes: > >Floating point numbers should not be used for counting except when such >use gains us a remarkable degree of badly-needed efficiency. > > count -=1; /* integer arithmetic with software test */ > if (count == 0) > abort_program; This is inefficient? I would hope that any decent compiler could turn these lines into the something like (usual case one instruction): / integer arithmetic with hardware test ISZ COUNT / PDP-8 instruction, but even CISC's like JMP ABORT / IBM 7090's and later have an equivalent -- carl lowenstein marine physical lab u.c. san diego {ihnp4|decvax|ucbvax} !sdcsvax!mplvax!cdl
leichter@yale.UUCP (Jerry Leichter) (02/20/88)
Commenting about the inplementation of SPITBOL/360, which used a floating point register to keep track of the number of statements executed, Doug Gwyn writes: > >So, the Snobol implementor made a stupid decision, then. > He the proceeds to explain that the decision was stupid (a) because when he compared two ways of doing this on a Gould Powernode, doing it with integers was faster; (b) the way SPITBOL did it was "non-portable". It's generally a good idea to understand the issues before criticizing a design decision. Neither Doug's comments, nor any of the others on this topic, are based on any such understanding. They appear to be made by people who don't understand the language, the implementation environment, the goals of the implementation --- the probably don't even know to within 5 years when the implementation they are criticising was done. How anyone can have the chutzpa to imagine that they have ANYTHING worthwhile to say, lacking all that basic information, would be beyond me if I hadn't seen so much flammage on this net already. To add a little reality: SPITBOL/360 was done somewhere around 1971. It was an attempt to produce a complete implementation of all of SNOBOL4 that would run as fast as possible. There was already a very portable --- certainly by the standards of the late 1960's, probably even by today's standards --- im- plementation of SNOBOL in existence. It took, by the standards of the day, a huge amount of memory, and ran slowly. SPITBOL attempted to address both constraints. It was willing to forgo portability, and was designed to run on IBM/360's --- yes, 360's, the 370 wasn't to exist for several more years --- under OS/360. Now, SNOBOL4 is not an easy language to execute efficiently. It allows for things like the creation of new functions, data types, and even variables at run time --- and these abilities are heavily used. It allows new code to be compiled at run time, sometimes replacing existing code. Assignments to, or even attempts to read from, variables can trigger I/O, or (via tracing) the execution of arbitrary user code. The fact that a particular access to a particular variable may have such affects can only be determined at run time. Enough generalities. The particulars of the use of a floating point statement counter are as follows: Floating point arithmetic is quite rare in SNOBOL programs, so there is little to be gained by keeping FP registers --- and remember, on a 360 FP registers are completely distinct from the general registers --- free. On the other hand, the nature of SNOBOL, and the way addressing works on a 360, places a premium on general registers. Also, in the early '70's, core was CORE --- registers were MUCH faster than main memory. Even FP registers and FP operations were quite fast compared to main memory accesses. Now, SNOBOL numbers statements, and it counts statements executed. The statement count must be checked at the beginning of every statement executed against a (user-changable, at run time) statement limit. The statement number, on the other hand, is only used in rare circumstances --- in generating errors, or in the (in practice) unlikely event that the user asks for it. So the SPITBOL implementors did something clever: They used an otherwise-unneeded resource (an FP register) in a clever way to solve several problems. The counter was kept in the FP register, and the hardware effectively did the statement limit check. Meanwhile, the actual instruction used --- an AUR, as Rahul Dhesi noted, Add Unnormalized Register --- was "unusual"; in fact, it was never generated by the compiler anywhere but at the head of a statement. Thus, it acted as a "statement begin" marker. No record was kept of the current statement number --- doing that would have required tying up another register, or an extra memory access, at each statement. Instead, on the rare occassions that the current statement number was needed, the run-time code would walk through all the compiled code --- it was all linked together --- and count AUR's until it got to the currently-executing statement. I believe there may have been other instances in which it was necessary to find the beginning of the current, or the next, statement; searching for an AUR could be used for that, too. All told, an elegant, effective, and fast solution, given the realities of the day. Some of the same people who design SPITBOL/360 later went on to develop MACRO/SPITBOL, a rather interesting implementation that was designed to be (a) portable; (b) efficient on any particular hardware. It succeeded; MACRO/SPITBOL on a PDP-10 was smaller, and usually faster, than SITBOL, an implementation that had been hand-tuned for the -10. MACRO/SPITBOL, now at least 10 years old, continues to be available; it runs on such machines as VAXes and 8086's, both designed well after the implementation. -- Jerry
dhesi@bsu-cs.UUCP (Rahul Dhesi) (02/20/88)
In article <7285@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: [re floating add versus integer decrement & test] >So, the Snobol implementor made a stupid decision, then. > >[On the] Gould PowerNode 9080, >which has a remarkably similar architecture...to the IBM 360's... >[a test showed that the] floating-point version burned 4.3 seconds of CPU >time while the integer counter version's overhead was only 0.5 seconds. I don't know what Dewar and Belcher, the authors of SPITBOL, would say to this. Here's what Griswold says in his book "The Macro Implementation of Snobol4" (Freeman & Co. 1972). "An amusing example of the difference between SNOBOL4 and SPITBOL is provided by the way &STLIMIT is implemented and statement numbers located....[In SNOBOL4] &STLIMIT (at a known location in UKYPBL) is checked by init1. If the statement limit has been exceeded, control is transferred to an error routine.... "SPITBOL, in its quest for efficiency, approaches these matters quite differently. At the beginning of each statement, the following instruction occurs. AUR SCNT,SINC This instruction increments a floating-point register reserved for statement counting. SCNT is set up so that when &STLIMIT is exceeded, a program interrupt occurs because of floating-point overflow. An interrupt handler detects this interrupt as excessive statement execution.... [some paragraphs later] "...In any event, SPITBOL is someone else's story to tell. It is a fascinating, well-designed, and cleverly implemented system which the serious student of software is well advised to study....SPITBOL, of course, is hardly machine-independent or portable. Heavy use is made of idiosyncracies of the 360 instruction set, and the coding is very tight." -- Rahul Dhesi UUCP: <backbones>!{iuvax,pur-ee,uunet}!bsu-cs!dhesi
gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/20/88)
In article <23450@yale-celray.yale.UUCP>, leichter@yale.UUCP (Jerry Leichter) writes: > To add a little reality: SPITBOL/360 was done somewhere around 1971. Yes, and this discussion about how to iterate via floating-point exception was being conducted in 1988. My concern was that people not use a "trick" unnecessarily, when the trick might not work and when the alternative is better anyway, in the C language, which is what this newsgroup is about. I mean, I've relied on the CDC 1700's ability to automatically indirect arbitrarily many times, and on -0 as a special flag, and other tricks, when programming specifically for that system (and when portable system programming was practically impossible anyway). But the vast majority of programmers are not in such circumstances today. There has been considerable progress in understanding the real economics of software since the 1960s and early 1970s; we have learned what is wrong with cute system-dependent "tricks" and assembly-language programming for general applications.
ok@quintus.UUCP (Richard A. O'Keefe) (02/20/88)
In article <722@mplvax.nosc.MIL>, cdl@mplvax.nosc.MIL (Carl Lowenstein) writes: > In article <2150@bsu-cs.UUCP> dhesi@bsu-cs.UUCP (Rahul Dhesi) writes: > >Floating point numbers should not be used for counting except when such > >use gains us a remarkable degree of badly-needed efficiency. > > count -=1; /* integer arithmetic with software test */ > > if (count == 0) abort_program; > This is inefficient? I would hope that any decent compiler could turn > these lines into the something like (usual case one instruction): > / integer arithmetic with hardware test > ISZ COUNT / PDP-8 instruction, but even CISC's like > JMP ABORT / IBM 7090's and later have an equivalent The /360 can do this with COUNT EQU {some register number} S COUNT,=1 BZ ABORT This does, however, involve a memory reference. If you don't want to do a memory reference, you can dedicate a register to holding 1, or you can keep the counter *negative* (no negative offsets!) and do LA COUNT,1(COUNT) * doesn't set the condition codes LTR COUNT,COUNT * so they must be set explicitly BZ ABORT Bearing in mind that the /360 places heavy demands on its general registers (immediate operands are scarce, there are no absolute address, address offsets are 0..4095, &c), maintaining the counter and its increment in floating-point registers could have contributed greatly to efficiency, not in this particular statement, but by making more general registers available elsewhere. The real point is that this was NOT a genuine instance of counting with floating-point values. What they *really* wanted was an integer count, using floating-point instructions was a hack which required exceptionally careful coding. The advice "don't write loops like for (x = 23.1; x <= 23.7; x += 0.1) .... " remains valid.
trt@rti.UUCP (Thomas Truscott) (02/20/88)
> In article <16982@watmath.waterloo.edu> rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes: > -> > auto size_t index; > -> > index = sizeof(array) / sizeof(array[0]); > -> > while (--index >= 0) > -... this is yet another > -way in which otherwise correct code will break when it is ported > -to an ANSI compiler (as I found out the hard way). The Gould compiler says "warning: unsigned >= always true". In article <7278@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: > The fact that the result of sizeof has an unsigned integral type is > not new with ANSI C. Yes, nor is it the cause of the infinite loop. By the way, the suggested fix: while (index-- > 0) causes the Gould compiler to say: "warning: degenerate unsigned comparison" which is reasonable since the fix is misleading. Better would be: while (index-- != 0) If your compiler does not do these things (and much much more!) complain to the vendor. Start writing SPRs!! You can spend the time now, or later ("the hard way"). Tom Truscott
jbn@glacier.STANFORD.EDU (John B. Nagle) (02/21/88)
Another classic problem is the fact that the difference between two unsigned values is itself unsigned. Thus, unsigned short a,b; a = 1; b = 2; if (a - b < 0) printf("B smaller than A\n"); is invalid. This particular strangeness caused an obscure bug in 4.3BSD networking which prevented interoperation of 4.3BSD's TCP with some other TCP implementations, including TOPS-20.