davidra@batcomputer.tn.cornell.edu (David Rabson) (07/29/89)
C does have a generalized "arithmetic if," if you wish, although it is
not functionally equivalent. Switch...case...case...default
is generally implemented as a table of branching addresses if the cases
are integers fairly close to each other and if there are enough cases
to make it pay.
Both gcc and Sun's cc do this if there are at least four cases with
contiguous integer tests (for instance, -1, 0, 1, and 2). Neither does
it if the cases are just -1, 0, and 1; I assume someone calculated the
tradeoff and found that the repeated tests were cheaper.
If your variable takes on values other than +/-1 and 0, of course,
you will not want to use switch...case...case to implement the
arithmetic goto.
As for using the results of a single TEST for two branch-if's,
our FORTRAN compiler does not do this; perhaps the trick fails
on a 68000, or maybe the compiler writers were too lazy to save
the extra nanosecond.
I think it's fairly obvious which of these pieces of code looks better.
(This is probably unnecessary, but I rarely get to exercise my
FORTRAN. Can anyone explain the error in the code?)
EXHIBITS:
PROGRAM MAIN
C
C NANOSECOND-WISE (MAYBE ON SOME MACHINES) BUT HOUR-FOOLISH
C
INTEGER I
READ(5,200) I
200 FORMAT(I)
IF (I) 1,2,3
1 WRITE(6,100)
100 FORMAT(20H0IT'S LESS THAN ZERO )
GOTO 4
2 WRITE(6,101)
101 FORMAT(10H0IT'S ZERO )
GOTO 4
3 WRITE(6,102)
102 FORMAT(23H0IT'S GREATER THAN ZERO )
4 STOP
END
C by the way, on my machine this program doesn't even work -- if
C I type in a positive number or zero, it gets it right, but if I
C type any negative number, it says it's zero.
main()
{
int i;
scanf("%d",&i);
if(i<0)
printf("It's less than zero\n");
else if(!i)
printf("It's zero\n");
else
printf("It's greater than zero\n");
/* or alternatively */
printf("It's%s zero\n", i?(i<0?" less than":" greater than"):"");
}
zdenko@csd4.milw.wisc.edu (Zdenko Tomasic) (07/29/89)
In article <8515@batcomputer.tn.cornell.edu> davidra@tcgould.tn.cornell.edu (David Rabson) writes: >C does have a generalized "arithmetic if," if you wish, although it is > .... (switch case , gcc and sun discussion omitted) >(This is probably unnecessary, but I rarely get to exercise my >FORTRAN. Can anyone explain the error in the code?) yes > PROGRAM MAIN > INTEGER I > READ(5,200) I >200 FORMAT(I) ^ This reserves integer field of only one digit/sign. when you use signed integer, sign is taken, but the magnitude (2nd character and beyond!) is not read! So you program get by fortran conversion -0 which is equivalent to 0. You either have to increase your integer field length(say I10) and right justify your input (trailing blanks within an integer field are treated as zeros!) or much more conveninetly, use list directed read, i.e. replace the read and format statement above with just READ(5,*) I >... (the rest of message deleted) -- ___________________________________________________________________ Zdenko Tomasic, UWM, Chem. Dept., P.O. Box 413, Milwaukee, WI 53201 UUCP: uwvax!uwmcsd1!uwmcsd4!zdenko ARPA: zdenko@csd4.milw.wisc.edu
mcdaniel@uicsrd.csrd.uiuc.edu (Tim McDaniel) (07/30/89)
This article has been crossposted to comp.lang.c (the original newsgroup) and comp.lang.fortran. Please send all followups to comp.lang.fortran. In article <8515@batcomputer.tn.cornell.edu> davidra@tcgould.tn.cornell.edu (David Rabson) writes: >200 FORMAT(I) In article <3600@csd4.milw.wisc.edu> zdenko@csd4.milw.wisc.edu (Zdenko Tomasic) writes: > This reserves integer field of only one digit/sign. FORTRAN 77, and presumably FORTRAN 66, do not allow the I format descriptor to appear without a width. On Alliant computers, an I without a width is permitted as an extension, and the width is assumed to be 7 characters for one- and two-byte integers, and 12 for 4-byte integers. > when you use signed integer, sign is taken, but the magnitude (2nd > character and beyond!) is not read! So you program get by fortran > conversion -0 which is equivalent to 0. What if you're on a one's complement machine? On those, -0 need not be the same as 0. VAX BSD 4.3, SUN OS 3.5, and Alliant Concentrix 3.0 all treat a single "-" in an otherwise blank field as 0. However, I have found no statement in my manuals guaranteeing it, and if I were writing a FORMAT conversion library, I'd consider making it illegal as an ill-formed number. (After all, these I = - ! FORTRAN i = -; /* C, C++ */ are not legal -- why not be consistent, if the standard permits it?) > You either have to increase your integer field length (say I10) Yes. > and right justify your input (trailing blanks within an integer > field are treated as zeros!) In FORTRAN 77, you can use the BLANK= attribute on the OPEN statement to control whether trailing blanks are ignored or treated as zeroes, by BLANK='NULL' or BLANK='ZERO' The default is NULL -- by default trailing blanks are simply ignored. I don't believe FORTRAN 66 took a stand on the issue, and in its default, implementors treated trailing blanks as zeroes. Briefly, trailing blanks *may be* treated as zeroes. > much more conveninetly, use list directed read, i.e. replace the > read and format statement above with just READ(5,*) I Quite true! It is much better for interactive input. -- "Let me control a planet's oxygen supply, and I don't care who makes the laws." - GREAT CTHUHLU'S STARRY WISDOM BAND (via Roger Leroux) __ \ Tim, the Bizarre and Oddly-Dressed Enchanter \ mcdaniel@uicsrd.csrd.uiuc.edu /\ mcdaniel%uicsrd@{uxc.cso.uiuc.edu,uiuc.csnet} _/ \_ {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel
davidsen@sungod.crd.ge.com (William Davidsen) (08/02/89)
Thanks to all the people who took the time to write about the lack of something filling the same function as the FORTRAN arithmetic IF. As someone pointed out, I'm not looking for arithmetic IF, but a way to do three separate things based on a value, usually the compare of two values in a sort or tree search. Some interesting ideas: ifcase (a < b) code; code; (a == b) more(code); (a > b) still(more); endcase; If I were going to make a really readable program, I could do this (and did, just to see how it looked and worked). #define LESS -1 #define EQUAL 0 #define GREATER 1 float T_tmp; #define COMPARE(a,b) ((T_tmp=a-b)==0 ? 0 : ((T_tmp>0)<<1)-1) For the person who said his FORTRAN didn't uses the flags set on a single test, that's poor compilation. I ran a program into the SunOS compiler (v3.5) and her's the program and partial output. m = 4 if (m) 1,2,3 1 print 5, m goto 4 2 print 10, m goto 4 3 print 15, m 4 stop 5 format(2H <, i5) 10 format(2h =, i5) 15 format(2H >, i5) end _______________ | | Partial generated code, comments are mine |________________ L11: movl #0x4,a5@(-0x8000) movl a5@(-0x8000),a6@(-0xc) tstl a6@(-0xc) ; one test jge L20 ; first jump jra L16 ; second jump L20: ; Fall into inline code tstl a6@(-0xc) jne L21 jra L17 L21: jra L18 L16: Again thanks for all the input, hopefully any new standard will come up with a reasonable way to write this, to (a) make it easy to read, (b) make it easy for compilers to generate good code (ie. recognize the special case). Obviously no one wants to have labels, but perhaps something like: n = (a-b $ -1 : 0 : 1); - or - return (a-ptr->val $ getv(ptr->prev : ptr->val : getv(ptr->next)); where there would be a "quadratic operator." No one could argue that this isn't in the spirit of the language, and an operator might be more easily added to the language than a control structure. bill davidsen (davidsen@crdos1.crd.GE.COM) {uunet | philabs}!crdgw1!crdos1!davidsen "Stupidity, like virtue, is its own reward" -me
hascall@atanasoff.cs.iastate.edu (John Hascall) (08/02/89)
In article <1429@crdgw1.crd.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes: >someone pointed out, I'm not looking for arithmetic IF, but a way to do >three separate things based on a value, usually the compare of two >values in a sort or tree search. > Some interesting ideas: > ifcase > (a < b) code; code; > (a == b) more(code); > (a > b) still(more); > endcase; This reminds my of an exec language I somtimes program in (no, you haven't heard of it), where you use: cases begin case (expression1) statement1 case (expression2) statement2 : : end And it evaluates each expression in order and executes the (possibly compound) statement of the first one (if any) which is true. [and you just use "case (true)" at the end for "default:"]. Of course, neither of these solve the original problem of how to execute a 3-way branch with a single expression evaluation. Imagine extending the concept: if (expr) s1; [else s2;] to: negative (expr) s1; [zero s2;] [positive s3;] I can hear the C-purists screaming in agony :-) John Hascall
chris@mimsy.UUCP (Chris Torek) (08/03/89)
(Short summary: the arithemtic `if' in the three different languages FIV, F66, and F77---usually each called `FORTRAN'---is syntactically ugly; various suggestions suggested and promptly ignored :-) .) Although I have described it here before, I might as well do it again; USENET has a short collective memory. The language Mesa (used at Xerox) has a construct that is a generalisation of C's `switch', called SELECT (Mesa keywords are in ALL CAPITALS, a practise I find ugly in the extreme, but fixable :-) ). One writes, for instance, SELECT TRUE FROM -- `FROM' might be `IN'; my Mesa is rusty a < b => signum _ -1; -- `_' is a left-arrow a = b => signum _ 0; -- and used for assignment. a > b => signum _ 1; -- (_ as arrow is `old ASCII') END; -- actually, I have forgotten the proper syntax -- for ending a select, but this will serve. Each argument to select (including the expression to be selected) is a regular Mesa expression; the first branch that matches (is equal) is taken. A C `switch' is thus simply SELECT expr FROM 1 => case1[]; -- [] is function call 2 => case2[]; 3 => case3[]; END; but by putting `true' in the select argument one can select against various conditions. A decent compiler will notice that the select expression is a constant (particularly for `true' or `false') and will generate the test(s) as the case expressions go by; it is easy to retain the condition codes across such tests. The `signum' example above should compile into cmp a,b jge not_first_case | skip if not less than mov #-1,signum jmp end_of_select not_first_case: jne not_second_case | skip if not equal mov #0,signum jmp end_of_select not_second_case: jle end_of_select | impossible, but who knows if | the compiler will figure that out. | one could write `TRUE' for the | last expression and eliminate | this. mov #1,signum end_of_select: -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
karl@haddock.ima.isc.com (Karl Heuer) (08/03/89)
In article <1429@crdgw1.crd.ge.com> davidsen@crdos1 (bill davidsen) writes: >...Again thanks for all the input, hopefully any new standard will come >up with a reasonable way to write this, to (a) make it easy to read, (b) >make it easy for compilers to generate good code (ie. recognize the >special case). I don't expect it'll ever make it into a C standard. It's useful, but not sufficiently useful to be worth the "feature pollution". >Obviously no one wants to have labels, but perhaps something like: > n = (a-b $ -1 : 0 : 1); >where there would be a "quadratic operator." This brings up something I haven't seen mentioned here yet. (a < b) is *not* equivalent to (a-b < 0) if the latter expression might overflow. (This was also a major flaw in the original fortran concept.) What you really want is a pentadic operator: n = (a ?? b -> -1 , 0 , 1); (Syntax flames to /dev/null; I'm not really proposing this.) Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint (PS: I think the word for 4-adic is "tetradic" or "quadary", not "quadratic".)
news@ism780c.isc.com (News system) (08/04/89)
In article <1287@atanasoff.cs.iastate.edu> hascall@atanasoff.cs.iastate.edu.UUCP (John Hascall) writes: >> Some interesting ideas: >> ifcase >> (a < b) code; code; >> (a == b) more(code); >> (a > b) still(more); >> endcase; > Imagine extending the concept: > > if (expr) s1; [else s2;] > > to: > > negative (expr) s1; [zero s2;] [positive s3;] > > > I can hear the C-purists screaming in agony :-) > >John Hascall As a language designer, I dislike this idea. I also dislike the C construct 'if (expr) s1; [ else s2;]' (borrowed from algol) for two reasons. First it requires the introduction of begin-end (sometimes spelled, {} ). This is required so that s1 and s2 can be more than a single statement. Second and more important, it causes the dangling 'else' problem (if s1 is an 'if' statement, which 'if' does the 'else' go with?). The proposal exacerbates the dangling 'else' problem. It gives us dangling 'zero' and 'positive', it introduces additional keywords, and it is less general than the 'ifcase' construct quoted above. Also the 'ifcase' construct eliminates the need for begin-end. Note that it is not hard (as compiler writing goes) to build a compiler which when given: if ((expr)<0) <statement-list> ((expr)=0) <statement-list> ((expr)>0) <statement-list> fi Would produce exact same code as for above proposal; assuming each <expr> is the same and has no side effects. And the above construct extends naturally to cover things like IEEE floating arithmetic e.g. if ((expr) = INFINITY) <statement-list> ((expr) = -INFINITY) <statement-list> ((expr)<0) <statement-list> ((expr)=0) <statement-list> ((expr)>0) <statement-list> else <statement-list> fi The 'else' case would be executed when the evaluation of <expr> producdes a NAN. I guess I am a C purist. I feel that adding warts to a toad will not make it more beautiful. Why not just leave C as it is? At least it is now a reasonably well defined and well documented language. And it is quite useful. Now lets hear the flames from the C-ophiles :-) Marv Rubinstein
john@basser.oz (John Mackin) (08/05/89)
In article <14181@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: > In article <1429@crdgw1.crd.ge.com> davidsen@crdos1 (bill davidsen) writes: > >Obviously no one wants to have labels, but perhaps something like: > > n = (a-b $ -1 : 0 : 1); > >where there would be a "quadratic operator." > (PS: I think the word for 4-adic is "tetradic" or "quadary", not "quadratic".) Don't think, find out! I won't bother quoting dictionary definitions as I am sure any of you who are interested in this can look them up for yourselves; but briefly: (1) `quadratic' is clearly incorrect; this is not one of its senses. (2) `tetradic' means `like a tetrad' and a tetrad is a set of four things regarded as a unity (I'm skipping lots of detail here), so that won't serve either. (3) `quadary' is, to my eyes and ears, a horrendously ugly word; futhermore I can't find it in any dictionary I have easy access to. Unfortunately, though, I think it's the best we've got; in technical areas one is sometimes forced to partake of neologisms, and I can't seem to come up with a more pleasing one than that. Suggestions are most welcome -- best by mail, as I think the question of what an operator of four operands should be called is perhaps of limited interest. John Mackin, Basser Department of Computer Science, University of Sydney, Sydney, Australia john@basser.oz.AU (john%basser.oz.AU@UUNET.UU.NET) {uunet,mcvax,ukc,nttlab}!munnari!basser.oz!john
kdb@chinet.chi.il.us (Karl Botts) (08/10/89)
>>someone pointed out, I'm not looking for arithmetic IF, but a way to do >>three separate things based on a value, usually the compare of two >>values in a sort or tree search. > >> Some interesting ideas: >> ifcase >> (a < b) code; code; >> (a == b) more(code); >> (a > b) still(more); >> endcase; What you want is to extend the grammar from: case-label constant-expression ':' to case-lobel expression ':' Many languages allow the equivalent of this. I don't think you'll see it soon in C, though -- there are a couple of conflicts. First, C puts restrictions on the grammar in a number of places to permit better optimization. Good C compilers go to a lot of work to figure out how to optimize case statements, often looking for consecutive or nearly consective strings of case values so jump tables can be constructed, figuring out how to test the variant for ranges to avaoid testing every value individually, and so forth. Arbitrary expressions would render most of this optimimiztion impossible. More importantly, C expressions can and do have wild and crazy side effects, but if the compiler is going to do any optimization at all, then the order in which the expressions (constant or otherwise) in the case labels is evaluated, or even whether or not any particular one is evaluated at all, must be undefined. Furthermore, it _is_ undefined in the current language definition. To make the side effects of arbitrary expressions in case-labels controllable, the order of their evaluation would have to be defined. This would be major, incompatible, change in the language.
dwh@twg-ap.UUCP (Dave Hamaker) (08/12/89)
In reference to a discussion which began from an expressed wish for something like the Fortran "Arithmetic IF" statement (for its clarity when you want to do different things with the <, ==, > cases of a test), in article <18868@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) describes Mesa's "SELECT" as a real world example of a language construct that is a generalization of C's "switch." One of my pet ideas concerns the generalization of things like "switch." There's one wrinkle to the idea which may be hard to do with current compiler technology. I just don't know enough about compilers to tell. But, there's a good chance some of you on the net do know, and would be willing to enlighten us. Applying the idea to switch gives: switch (lhs) { case (rhs1): statements case (rhs2): statements ... case (rhsN): statements default: statements } where: the "(lhs)" can be omitted; if present, "lhs" is an expression, or a *part* of one; ("lhs" stands for "left-hand side"); the "rhs's" are also expressions, or *parts* of same; the parentheses around a "rhs" may be omitted if it does not contain a colon (":"); ("rhs" stands for "right-hand side"); neither the "lhs", nor any "rhs", may contain part of a: character constant, string constant, parenthesized sub-expression, or any other token (this could be relaxed, but it probably would not be wise to do so). If "lhs" is a complete (non-null) expression, for each "rhs" which is a complete expression, the text of "lhs" and "rhs" are concatenated around the text of an "==" operator and the result is evaluated as an expression. Otherwise, the text of "lhs" and "rhs" are just concatenated and evaluated as an expression. Control is transferred to the first case with a non-zero (true) expression value, otherwise to the "default" label, if present, or the statement following the switch construct, if not (but see my "aside"). If "lhs" is a complete expression and all "rhs's" are constants, "lhs" must be evaluated exactly once; otherwise, "lhs" (or parts of it) may be evaluated more than once, and the constructed case expressions may be evaluated in any order (even in parallel) and need not all be evaluated, at the complete discretion of the compiler. This means that case expressions with side effects cause undefined results. Note also that duplicate case expressions are legal, but that unreachable cases which are detectable at compile time certainly deserve warning diagnostics. With this, we could write things like: switch (x) { case > 0: signum = 1; break; case 0: signum = 0; break; case < 0: signum = -1; break; } and: switch (p < ) { case .1: ... case .5: ... default: ... } Now, it seems pretty straightforward to me to compile the construct as follows: First, reserve space for a jump instruction. Second, follow this with code generated for the "statements" as the case construct is scanned. As this is done, remember the "lhs" and the "rhs's," and remember where in the generated code each "rhs" points to. Classify the "lhs" and each "rhs" as to whether or not it is a valid expression (by trying to parse them). Further classify each valid "rhs" expression as to whether or not it is a constant expression. Third, when the end of the switch is reached, reserve space for another jump instruction. Fourth, follow this with code generated to evaluate the expressions constructed from the "lhs" and "rhs's" and to jump to the proper point in the generated code associated with each case label. Finally, fill in the first reserved jump slot to go to the evaluation code, and fill in the second reserved jump slot to transfer to the end of the switch. The wrinkle I'm worried about is: is this notion of saving some program text aside, concatenating expressions out of them and then "reparsing" the internally constructed expressions something you can build with current compiler-construction tools? Comments from those in the know? Aside: If I had my druthers, I'd have switches without a default case give undefined results when no case matches. It would be a promise to the compiler that one of the cases will match, allowing generation of "leaner" code in that event (perhaps with a warning, which would also help catch when you mistype "default"). I like sharp tools, but it would not be appropriate to change this sort of thing in an existing construct (perhaps a syntax for specifying no default could be added). -Dave Hamaker dwh@twg.com ...!sun!amdahl!twg-ap!dwh
stevenw@oakhill.UUCP (Steven Weintraub) (08/14/89)
There have been a lot of articles about generalizing the case statement to take expressions instead of constants on the case labels. It should be pointed out this feature is already there. A case statement like this is nothing more than an if-else if-else and any compiler will have to handle such a case statement as such. For example take the the following case (no pun intended). > Some interesting ideas: > ifcase > (a < b) code; code; > (a == b) more(code); > (a > b) still(more); > endcase; is nothing more than : if (a < b) { code ; code; } else if (a == b) { more(code); } else if (a > b) { still(more) } In article <359@twg-ap.UUCP>, dwh@twg-ap.UUCP (Dave Hamaker) writes: > Applying the idea to switch gives: > > switch (lhs) { > case (rhs1): statements > case (rhs2): statements > ... > case (rhsN): statements > default: statements > } > > where: > > the "(lhs)" can be omitted; > if present, "lhs" is an expression, or a *part* of one; > ("lhs" stands for "left-hand side"); > > the "rhs's" are also expressions, or *parts* of same; > the parentheses around a "rhs" may be omitted if it does > not contain a colon (":"); > ("rhs" stands for "right-hand side"); > > neither the "lhs", nor any "rhs", may contain part of a: > character constant, > string constant, > parenthesized sub-expression, or > any other token > (this could be relaxed, but it probably would > not be wise to do so). > You say that the 'lhs' can be an incomplete expression or an complete expression. If the 'lhs' is an incomplete expression there are several problems. First it is extemely hard to write the syntax for a partial expression. Add to that all you are doing with the partial expression is holding a pointer to a partial expression tree, which just has to completed at each label anyway. You might as well use an if-else if structure to do this because you are not saving any code at cost of complicating the languge. (I can see some tricks that could be used to generate this tree, but it would greatly complicate the parser). If the 'lhs' is a complete expression, then this statement is nothing more than an extra if-else if structure. If your worry is evaluation the statement more than once, then you can always store the expression in a temporary variable before (or in the first expression) of the if statement. > switch (x) { > case > 0: > signum = 1; > break; > case 0: > signum = 0; > break; > case < 0: > signum = -1; > break; > } becomes (assuming x to be a complicated expression) : temp = (x); if (temp > 0) or if ((temp = (x)) > 0) signum = 1; else if (temp == 0) signum = 0; else if (temp < 0) signum = -1; > and: > switch (p < ) { > case .1: > ... > case .5: > ... > default: > ... > } generates the same code as: if (p == .1) ... else if (p == .5) ... else ... (again p can be place in a temporary if only one evaluation is desired) The reason for a case statement is that with simple constants as the goals an if-else if structure can go under a great deal of optimization. Thus the case statement is used to tell the compiler this can happen. To widen the case statement to take expressions or part of expression, you are adding nothing more than the if-else if structure you already have at the cost of losing the case structure, whose purpose is to tell the compiler it can optimize (Otherwise you'd just have an if-else if). enough from this mooncalf - Steven ---------------------------------------------------------------------------- These opinions aren't necessarily Motorola's or Remora's - but I'd like to think we share some common views. ---------------------------------------------------------------------------- Steven R Weintraub | O Lord, ...!cs.utexas.edu!oakhill!stevenw | let me talk gently, Motorola Inc. Austin, Texas | for I might have to eat my (512) 891-3023 (office) (512) 453-6953 (home) | words tomorrow. ----------------------------------------------------------------------------