From 2978396775934081361
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: 109fba,9a5d91576ea14293
X-Google-Attributes: gid109fba,public
X-Google-Thread: f78e5,42ca30940288d269,start
X-Google-Attributes: gidf78e5,public
X-Google-ArrivalTime: 1992-02-28 14:27:45 PST
Xref: sparky comp.lang.c++:5384 comp.std.c++:246
Path: sparky!uunet!think.com!ames!agate!forney.berkeley.edu!jbuck
From: jbuck@forney.berkeley.edu (Joe Buck)
Newsgroups: comp.lang.c++,comp.std.c++
Subject: Re: X to the Y power, how?
Date: 28 Feb 1992 22:19:45 GMT
Organization: University of California, Berkeley
Lines: 229
Distribution: world
Message-ID: <omci1INN8np@agate.berkeley.edu>
References: <50175@hydra.gatech.EDU> <1992Feb27.135250.25403@ux1.cso.uiuc.edu> <1992Feb27.223558.276@rice.edu> <22292@alice.att.com>
NNTP-Posting-Host: forney.berkeley.edu

In article <22292@alice.att.com>, bs@alice.att.com (Bjarne Stroustrup) writes:
|> That is the rub. We don't know. To the best of my knowledge nobody has
!> bothered to think up and describe a specific proposal in detail and evaluate
!> its implications. Naturally, we want an exponentiation operator, naturally
!> we want lots of things, we all have wish lists. The problem is that we can't
!> have everything we want and that having to be precise about what we want
!> might spoil some of the fun.

OK, I decided to take that as a challenge.  I don't find it at all
difficult to be precise about the exponentiation operator.

Here is the start of a more formal proposal.  I welcome feedback and
comments, and if it seems that people are interested it could be made
into an official proposal.

Draft Proposal to add an exponentiation operator to C++

Version 1
February 28, 1992
Joseph T. Buck (jbuck@ohm.berkeley.edu)

The portion of the grammar described on p. 72 of the ARM is revised as
follows:

exp-expression:
	pm-expression
	pm-expression @ exp-expression

multiplicative-expression:
	exp-expression
	multiplicative-expression * exp-expression
	multiplicative-expression / exp-expression
	multiplicative-expression % exp-expression

The revision introduces a new operator, @, which is right-associative
and binds more tightly than the multiplication/division operators
(*, /, and %), and more loosely than .*, ->*, cast operators, or
unary operators.  The grammar change basically sticks a new production
in between pm-expression and multiplicative-expression.

The exponentiation operator, @, groups right-to-left.  The operands
of @ must have arithmetic type.

The "usual arithmetic conversions" of section 4.5 are used, with the
following exception:

If the first argument is of one of the types long double, double, or
float, and the second argument is of integral type, integral promotion
(ch 4.1) takes place on the second argument, but no change is made to
the first argument.

	Commentary: we wish to allow -2.0@i, but not -2.0@x, where i
	is of integral type and x is of floating type.

The effect of these rules is to allow the following combinations of
arguments and result types:

	NOTE: I know that the language does not permit overloading
	on builtin arguments.  I am showing the "simulated prototypes"
	for the argument combinations that can occur.

Group 1:
long double operator@(long double,long double);
double operator@(double,double);
float operator@(float,float);

Group 2:
long double operator@(long double,long int);
long double operator@(long double,unsigned int);
long double operator@(long double,int);
double operator@(double,long int);
double operator@(double,unsigned int);
double operator@(double,int);
float operator@(float,long int);
float operator@(float,unsigned int);
float operator@(float,int);

Group 3:
long int operator@(long int,long int);
unsigned int operator@(unsigned int,unsigned int);
int operator@(int,int);

For all three groups, the result when both arguments are zero
is undefined.

For group 1 (both arguments of floating type), if the first argument
is negative the result is undefined.  For group 2 (floating base
raised to integral power), the first may be negative as long as
the second argument is zero or positive.  If both arguments are
negative the result is undefined.  For group 3, the result of a
negative second argument is undefined.

	Commentary: "undefined" means that an arithmetic exception
	may occur, or that the result may be garbage, for example,
	0@0 might return 1, 0, or an error.

	I'm choosing "undefined", rather than specifying exceptions,
	to be consistent with how the ARM deals with things like
	division by zero.

-------------------------------------------------------------------

"class complex" is not part of the standard at this point.  Should
it be standardized, the following operator overloads might be used:

complex operator@(complex b,complex e) {
	return exp(e*log(b));
}

// optional: might be desirable because log(double) is cheaper
complex operator@(double b,complex e) {
	return exp(e*log(b));
}

-------------------------------------------------------------------

Now for the objections and questions:

1.  But -1@0.5 is complex(0,1).  Why shouldn't that result be returned?

It is inconsistent with the language for the type of the result to
depend on the value of the arguments.  If a user wants to deal with
complex results, it's not difficult to specify complex classes.  Every
other operator in the language restricts the result to be no more
general than the type of its arguments; e.g. 3/2 = 1, not 1.5, even
though 1.5 is "correct".

2.  But nevertheless, complex(0,1) is the right answer.  Users won't
accept this.

Experience with Fortran suggests otherwise.  The semantics I've chosen
generally agree with Fortran, and yet everything fits nicely with C++
conventions.

3.  You're trying to make my favorite language more like Fortran, and
Fortran is an inferior language.

You're not going to get a lot of the people who use Fortran to convert to
C++ as long as you make life difficult for them.  It's not just a matter
of syntax; most compilers are simply going to generate worse code if there
isn't an exponentiation operator, unless the users write their code more
carefully than we can expect.  Fortran is inferior in many ways, but there
is a reason why it's still used for large scientific problems, and the
exponentiation operator is one of the reasons (vectorizability is another,
but that's another argument).

4.  Why don't you just use the "usual arithmetic conversions"?  Why the
exception?

Because users will be unhappy if X@2 returns an exception for negative
X, when it's perfectly well-defined.

5.  Why isn't it enough to use "pow", especially since you can overload it
to define pow(double,double), pow(double,int), and pow(int,int)?

Most scientific codes contain large amounts of exponentiation; raising
a real exponent to an integer power where the integer is known at compile
time is common, but real or integer unknown exponents are also common.
Evaluation of polynomials (where x@1, x@2, ... are used in sequence) is
a common operation.  Forcing the use of pow(...) has several harmful effects:

	The code size increases and complicated expressions become more
	difficult to read.  C/C++ programmers who don't think this is
	a problem haven't seen scientific codes, where even with the
	exponentiation operator, expressions can require several lines
	to write.

	Strength reduction becomes much more difficult to apply, unless
	pow(...) is made a special function known by the compiler.

	Optimizations commonly applied in Fortran compilers when the same
	base appears with several constant exponents are also more difficult.

6.  Why not use ** as in Fortran?

It would break "int main(int argc,char **argv)".  We can't make ** a
token.

7.  How about ^, ^^, or #.

^ is taken (exclusive-or).

I could live with ^^, but there is a relation between & and && and |
and || that might suggest a completely different meaning for ^^ to some
(a logical rather than a bitwise exclusive-or).

# doesn't suggest the right thing to me; also it might confuse some
preprocessor implementations if it appeared as the first nonblank
character on a line.

8.  It will be hard to implement.

I disagree.  The modification to the grammar is simple.  The single
exception to the "usual arithmetic conversions" rule is not difficult
to check for.  A quick and dirty implementation can simply insert
function calls for the various templates shown above, and their number
can be reduced in many environments; strength reduction for common
cases (exponent is a small constant integer) is very easy to apply.
Compared to, say, implementing exceptions, the work required for this
change will be trivial.

9.  But for implementations like cfront, which produce C, the lack of
an exponentiation operator in C is a problem.

Just as overloaded operators for classes are turned into function calls
unless they are inlined, a cfront-like implementation could inline
some cases (turning x@2 into x*x, say) and generate function calls
for the rest.  cfront already generates function calls for user-defined
operators; @ could be treated the same way even when used on builtin
types.

10.  What is the effect on existing programs?

None.  "@" is not used in the current grammar; no existing program
will break.

11.  Are there any other benefits or impact?

A new operator is available for overloading, with an intuitive meaning
("at") that may suggest natural uses for some classes.

If we attract more Fortran users they might ask us to implement
EQUIVALENCE next. :-)




-- 
Joe Buck	jbuck@ohm.berkeley.edu


