## digitalmars.D - Semantics of ^^

• Don (47/47) Dec 08 2009 Based on everyone's comments, this is what I have come up with:
• bearophile (13/23) Dec 08 2009 You can rewrite both of those as:
• Don (7/36) Dec 08 2009 That's an interesting one.
• bearophile (4/6) Dec 08 2009 Probably not :-)
• BCS (2/47) Dec 08 2009 Maybe. I think it's worth looking into.
• KennyTM~ (5/28) Dec 08 2009 Other non-essential special cases are 2^^n == 1<
• Denis Koroskin (3/50) Dec 08 2009 Looks solid. Just a quick question: is ^^ going to stay as opPow, or is ...
• Lars T. Kyllingstad (9/58) Dec 08 2009 This is reasonable.
• Steven Schveighoffer (21/33) Dec 08 2009 If x and y are both integral and x is 2, then the operation becomes 1 <<...
• Don (6/26) Dec 08 2009 And if x is 4, it becomes 1 << 2*y, etc.
• Steven Schveighoffer (10/37) Dec 08 2009 2^^n would be a very common entity in programming, it's definitely much ...
• Bill Baxter (39/68) Dec 08 2009 Is that consistent with math? I think in math they usually write
• Phil Deets (6/33) Dec 08 2009 I was also wondering about this. If it can't be detected at compile time...
• Phil Deets (4/7) Dec 08 2009 Oops, ~(n&1)+1 doesn't work, but ~((n&1)<<1)+2 does.
• Bill Baxter (4/9) Dec 08 2009 Right, and you can always write x^^y as pow(x,y). A major point of
• Don (2/24) Dec 08 2009 No, it's a compile-time error.
• Phil Deets (9/34) Dec 08 2009 It can't be a compile-time error if it can't be detected at compile time...
• Phil Deets (5/38) Dec 08 2009 Maybe you meant, if you can't prove it's positive, it's an error. If so,...
• Don (3/47) Dec 08 2009 Read the proposal. It's a runtime divide error.
• Don (9/86) Dec 08 2009 No. 0^^0 is 1, as well.
• Bill Baxter (17/58) Dec 09 2009 Is it? That's rather embarrassing for Wolfram Alpha, then (and
• Don (3/16) Dec 08 2009 Hmm. Several other languages give it that precedence. But you're right,
• Lars T. Kyllingstad (5/22) Dec 09 2009 Good catch, I missed that one in the original proposal. Between unary
• Don (26/26) Dec 09 2009 CHANGES BASED ON FURTHER COMMENTS
• Rainer Deyke (9/12) Dec 09 2009 I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
• Lars T. Kyllingstad (4/15) Dec 09 2009 I don't think it's a problem: Either it works as expected, or it causes
• Rainer Deyke (5/15) Dec 09 2009 Not quite. Under the proposal, -1^^-1 works (i.e. produces the correct
• Don (2/15) Dec 09 2009 It won't pass CTFE.
• Rainer Deyke (9/14) Dec 09 2009 pure int f() { return -1; }
• Don (17/30) Dec 09 2009 No, it doesn't work. It's exactly the same as:
• Don (13/56) Dec 09 2009 You can see this effect in action in the current version of DMD, because...
• Rainer Deyke (9/11) Dec 09 2009 That's a very fine distinction. One that may not survive future
• Don (14/24) Dec 09 2009 Yes. This is a very narrow special case. Bill Baxter and others have
• Lars T. Kyllingstad (5/20) Dec 09 2009 That depends on which of these two rules gets the higher precedence:
• Don (3/17) Dec 09 2009 No. That's why I wrote it as a transformation rule. CTFE is not
• Simen kjaeraas (7/36) Dec 09 2009 Might I enquire as to why the exponent should be allowed to be signed
• KennyTM~ (4/43) Dec 09 2009 Because 3^^-1 would become 3^^4294967295 (note: int can be implicitly
• bearophile (5/6) Dec 09 2009 I agree, no unsigned values, please.
• Andrei Alexandrescu (6/56) Dec 09 2009 On a non-degenerate base, any exponent greater than 32 (for int) and 64
• Andrei Alexandrescu (4/6) Dec 09 2009 In fact probably all exponents can be special-cased to use the minimal
• Don (8/53) Dec 09 2009 Because I think it would be too restrictive. Consider code like this:
• KennyTM~ (2/28) Dec 09 2009 0^^-1 == 0 ?
• Don (2/39) Dec 09 2009 Good catch. That line is completely wrong.
• Don (6/39) Dec 09 2009 Stuff it, it's too hard to explain.
• Jason House (8/60) Dec 09 2009 I think you have a bad corner case:
• Jason House (2/67) Dec 09 2009
• Don (2/11) Dec 09 2009 No, they will all work. Both rt and ct are manifest constants.
• Don (10/22) Dec 09 2009 (Actually it's a bit of wierd compiler quirk that const/immutables
Don <nospam nospam.com> writes:
```Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at compile
time, if possible.
* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce. In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the
compiler to use range propagation on the value of y to detect most
exponentiation errors at compile time. (If those cases are legal, the
compiler can't generate an error on x^^-2, because of the possibility
that x might be 1 or -1).
Also note that making it an error leaves open the possibility of
changing it to a non-error later, without breaking code; but going from
non-error to error would be more difficult.

(2) USE OF THE INTEGER DIVIDE ERROR
Note that on x86 at least, a hardware "integer divide error", although
commonly referred to as "division by zero", also occurs when the DIV
instruction, which performs uint = ulong/uint, results in a value
greater than uint.max. Raising a number to a negative power does involve
a division, so it seems to me not unreasonable to use it for this case
as well.
Note that 0 ^^ -1 is a division by zero.
This means that, just as you should check that y!=0 before performing
x/y, you should check that y>=0 before performing x^^y.

(3) OVERFLOW
int ^^ int returns an int, not a long. Although a long would allow
representation of larger numbers, even doubling the number of bits
doesn't help much in avoiding overflow, because x^^y is exponential.
Even a floating-point representation can easily overflow:
5000^5000 easily overflows an 80-bit real.
So, it's preferable to retain the simplicity that typeof(x^^y) is
typeof(x*y).
```
Dec 08 2009
bearophile <bearophileHUGS lycos.com> writes:
```Don:

Based on everyone's comments, this is what I have come up with:

Looks good.

* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }

You can rewrite both of those as:
{ typeof(x) u = 1; foreach (i; 0 .. y) { u *= x; } return u; }

(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

This is not essential:
(-1)**n is a common enough shortcut to produce an alternating +1 -1, you can
see it used often enough in Python code (and in mathematics). This search gives
433 results:
When used for this purpose (-1) is always compile time constant, so the
compiler can grow a simple rule the rewrites:
(-1) ^^ n
as
(n & 1) ? -1 : 1

Bye,
bearophile
```
Dec 08 2009
Don <nospam nospam.com> writes:
```bearophile wrote:
Don:

Based on everyone's comments, this is what I have come up with:

Looks good.

* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }

You can rewrite both of those as:
{ typeof(x) u = 1; foreach (i; 0 .. y) { u *= x; } return u; }

(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

This is not essential:
(-1)**n is a common enough shortcut to produce an alternating +1 -1, you can
see it used often enough in Python code (and in mathematics). This search gives
433 results:
When used for this purpose (-1) is always compile time constant, so the
compiler can grow a simple rule the rewrites:
(-1) ^^ n
as
(n & 1) ? -1 : 1

That's an interesting one.
With this proposal, that optimisation could still be made when it is
known that n>=0. We *could* make a special rule for compile-time
constant -1 ^^ n, to allow the optimisation even when n<0. But then you
have to explain why:  x = -1; y = x^^-2; is illegal, but y = -1^^-2 is
legal. Can that be justified?
```
Dec 08 2009
bearophile <bearophileHUGS lycos.com> writes:
```Don:

But then you have to explain why:  x = -1; y = x^^-2; is illegal,
but y = -1^^-2 is legal. Can that be justified?

Probably not :-)

Bye,
bearophile
```
Dec 08 2009
BCS <none anon.com> writes:
```Hello Don,

bearophile wrote:

Don:

Based on everyone's comments, this is what I have come up with:

Looks good.

* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }

You can rewrite both of those as:
{ typeof(x) u = 1; foreach (i; 0 .. y) { u *= x; } return u; }
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase
in
complexity which they introduce.

This is not essential:

(-1)**n is a common enough shortcut to produce an alternating +1 -1,
you can see it used often enough in Python code (and in mathematics).
This search gives 433 results:

+lang%3Apython

When used for this purpose (-1) is always compile time constant, so
the compiler can grow a simple rule the rewrites:

(-1) ^^ n

as

(n & 1) ? -1 : 1

That's an interesting one.
With this proposal, that optimisation could still be made when it is
known that n>=0. We *could* make a special rule for compile-time
constant -1 ^^ n, to allow the optimisation even when n<0. But then
you
have to explain why:  x = -1; y = x^^-2; is illegal, but y = -1^^-2 is
legal. Can that be justified?

Maybe. I think it's worth looking into.
```
Dec 08 2009
KennyTM~ <kennytm gmail.com> writes:
```On Dec 8, 09 19:16, bearophile wrote:
Don:

Based on everyone's comments, this is what I have come up with:

Looks good.

* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y>  0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }

You can rewrite both of those as:
{ typeof(x) u = 1; foreach (i; 0 .. y) { u *= x; } return u; }

(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

This is not essential:
(-1)**n is a common enough shortcut to produce an alternating +1 -1, you can
see it used often enough in Python code (and in mathematics). This search gives
433 results:
When used for this purpose (-1) is always compile time constant, so the
compiler can grow a simple rule the rewrites:
(-1) ^^ n
as
(n&  1) ? -1 : 1

Bye,
bearophile

Other non-essential special cases are 2^^n == 1<<n and x^^2 == x*x, but
these should be put in the optimizer, not the language. (And an integer
power algorithm http://www.c2.com/cgi/wiki?IntegerPowerAlgorithm should
be used instead of a simple foreach loop implementation-wise.)
```
Dec 08 2009
"Denis Koroskin" <2korden gmail.com> writes:
```On Tue, 08 Dec 2009 13:32:26 +0300, Don <nospam nospam.com> wrote:

Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at compile
time, if possible.
* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce. In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the
compiler to use range propagation on the value of y to detect most
exponentiation errors at compile time. (If those cases are legal, the
compiler can't generate an error on x^^-2, because of the possibility
that x might be 1 or -1).
Also note that making it an error leaves open the possibility of
changing it to a non-error later, without breaking code; but going from
non-error to error would be more difficult.

(2) USE OF THE INTEGER DIVIDE ERROR
Note that on x86 at least, a hardware "integer divide error", although
commonly referred to as "division by zero", also occurs when the DIV
instruction, which performs uint = ulong/uint, results in a value
greater than uint.max. Raising a number to a negative power does involve
a division, so it seems to me not unreasonable to use it for this case
as well.
Note that 0 ^^ -1 is a division by zero.
This means that, just as you should check that y!=0 before performing
x/y, you should check that y>=0 before performing x^^y.

(3) OVERFLOW
int ^^ int returns an int, not a long. Although a long would allow
representation of larger numbers, even doubling the number of bits
doesn't help much in avoiding overflow, because x^^y is exponential.
Even a floating-point representation can easily overflow:
5000^5000 easily overflows an 80-bit real.
So, it's preferable to retain the simplicity that typeof(x^^y) is
typeof(x*y).

Looks solid. Just a quick question: is ^^ going to stay as opPow, or is it
about to join the opBinary party?
```
Dec 08 2009
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
```Don wrote:
Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at compile
time, if possible.
* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce. In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the
compiler to use range propagation on the value of y to detect most
exponentiation errors at compile time. (If those cases are legal, the
compiler can't generate an error on x^^-2, because of the possibility
that x might be 1 or -1).
Also note that making it an error leaves open the possibility of
changing it to a non-error later, without breaking code; but going from
non-error to error would be more difficult.

I agree.

(2) USE OF THE INTEGER DIVIDE ERROR
Note that on x86 at least, a hardware "integer divide error", although
commonly referred to as "division by zero", also occurs when the DIV
instruction, which performs uint = ulong/uint, results in a value
greater than uint.max. Raising a number to a negative power does involve
a division, so it seems to me not unreasonable to use it for this case
as well.
Note that 0 ^^ -1 is a division by zero.
This means that, just as you should check that y!=0 before performing
x/y, you should check that y>=0 before performing x^^y.

This is reasonable.

(3) OVERFLOW
int ^^ int returns an int, not a long. Although a long would allow
representation of larger numbers, even doubling the number of bits
doesn't help much in avoiding overflow, because x^^y is exponential.
Even a floating-point representation can easily overflow:
5000^5000 easily overflows an 80-bit real.
So, it's preferable to retain the simplicity that typeof(x^^y) is
typeof(x*y).

Absolutely. I think people who use exponentiation will be well aware of
how easily it overflows, and will use long or BigInt (which should of
most common uses of int^^int will be x^^2, x^^3, and x^^4.

It looks like you've given this quite some thought. Thanks!

-Lars
```
Dec 08 2009
"Steven Schveighoffer" <schveiguy yahoo.com> writes:
```On Tue, 08 Dec 2009 05:32:26 -0500, Don <nospam nospam.com> wrote:

Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at compile
time, if possible.
* If either x or y are floating-point, the result is pow(x, y).

If x and y are both integral and x is 2, then the operation becomes 1 << y

Also, what happens when you do 3^^1000000?  I hope this does not result in
the exact loop you wrote above.

At the very least, when y > 32, the following code is more efficient:

{
/* get largest set bit, probably could do this more efficiently, note
that the type of m and y should be unsigned */
typeof(y) m = 1 << (typeof(y).sizeof * 8 - 1);
while(m > y)
m >>= 1;

long u = 1;
for(; m > 0; m >>=1)
{
u *= u;
if(m & y)
u *= x;
}
return u;
}

-Steve
```
Dec 08 2009
Don <nospam nospam.com> writes:
```Steven Schveighoffer wrote:
On Tue, 08 Dec 2009 05:32:26 -0500, Don <nospam nospam.com> wrote:

Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at
compile time, if possible.
* If either x or y are floating-point, the result is pow(x, y).

If x and y are both integral and x is 2, then the operation becomes 1 << y

And if x is 4, it becomes 1 << 2*y, etc.
sequences, it's a mathematical research area.

Also, what happens when you do 3^^1000000?  I hope this does not result
in the exact loop you wrote above.

No, of course not. I was just describing semantics, not implementation.
The little foreach loop implicitly describes what happens to overflow, etc.
```
Dec 08 2009
"Steven Schveighoffer" <schveiguy yahoo.com> writes:
```On Tue, 08 Dec 2009 10:17:30 -0500, Don <nospam nospam.com> wrote:

Steven Schveighoffer wrote:
On Tue, 08 Dec 2009 05:32:26 -0500, Don <nospam nospam.com> wrote:

Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at
compile time, if possible.
* If either x or y are floating-point, the result is pow(x, y).

If x and y are both integral and x is 2, then the operation becomes 1
<< y

And if x is 4, it becomes 1 << 2*y, etc.
sequences, it's a mathematical research area.

2^^n would be a very common entity in programming, it's definitely much
more appealing to me than 1 << n.  I just figured the compiler should
avoid making the optimizer work on optimizing out that foreach loop and do
the work up front.

But in any case, since I misunderstood what you meant (that the above loop
is not literally inserted), it doesn't matter as long as the
implementation can be optimized.

Also, what happens when you do 3^^1000000?  I hope this does not result
in the exact loop you wrote above.

No, of course not. I was just describing semantics, not implementation.
The little foreach loop implicitly describes what happens to overflow,
etc.

ok good.

-Steve
```
Dec 08 2009
Bill Baxter <wbaxter gmail.com> writes:
```On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

Is that consistent with math?   I think in math they usually write
(-1)^n with parens.
See for example the sin power series here:
http://en.wikipedia.org/wiki/Power_series

What's the rationale for going against math here?

* The type of x ^^ y is the same as the type of x * y.

Like it.

* If y =3D=3D 0, =A0x ^^ y is 1.

Need to mention what happens when x is also 0.

* If both x and y are integers, and y > 0, =A0x^^y is equivalent to
=A0 { auto u =3D x; foreach(i; 1..y) { u *=3D x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error occurs=

,
regardless of the value of x. This error is detected at compile time, if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.  I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
=A0* If x =3D=3D 1, =A0x ^^ y is 1
=A0* If x =3D=3D -1 and y is even, x^^y =3D=3D 1
=A0* If x =3D=3D -1 and y is odd, x^^y =3D=3D -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

Hmm, I'm not so sure about that.  I saw examples of this being used
even in the small sampling of search results from Python and Fortran
code that I looked at.  Many mathematical constructs are defined as
having a leading sign of (-1)^^n  (like the sin series formula linked
above).

In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the compiler=

to
use range propagation on the value of y to detect most exponentiation err=

ors
at compile time. (If those cases are legal, the compiler can't generate a=

n
error on x^^-2, because of the possibility that x might be 1 or -1).

I didn't see this error as adding much value when there was nothing
clearly lost from it.  But here you're showing there is a real price
to pay for this nanny compiler behavior.   To me that makes the error
clearly not worth the price of admission.  I also think that since 0^0
and 0^-1 and such are mathematically undefined, careful users of opPow
will already have to put some if() check before blindly doing an x^^y.
if(x!=3D0 || y!=3D0)  x^^y;
the people who care can just change the check to
if(x!=3D0 || y>0) x^^y;

Doesn't changing that one operator there doesn't seem like an undue
burden upon those who are careful checkers of their values.

Also note that making it an error leaves open the possibility of changing=

it
to a non-error later, without breaking code; but going from non-error to
error would be more difficult.

I think it's pretty clear that the error goes too far right now
without taking a wait-and-see stance.

--bb
```
Dec 08 2009
"Phil Deets" <pjdeets2 gmail.com> writes:
```On Tue, 08 Dec 2009 12:42:33 -0500, Bill Baxter <wbaxter gmail.com> wrote:

On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
[snip]
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs,
regardless of the value of x. This error is detected at compile time, if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.  I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

is the result 0?

[snip]
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

Hmm, I'm not so sure about that.  I saw examples of this being used
even in the small sampling of search results from Python and Fortran
code that I looked at.  Many mathematical constructs are defined as
having a leading sign of (-1)^^n  (like the sin series formula linked
above).
[snip]

I think n is always non-negative in the trig series, but some Laurent
series use negative n values. So (-1)^^n might be useful for negative n,
but you could always rewrite it as (n%2 ? -1 : 1) or ~(n&1)+1.
```
Dec 08 2009
"Phil Deets" <pjdeets2 gmail.com> writes:
```On Tue, 08 Dec 2009 16:44:46 -0500, Phil Deets <pjdeets2 gmail.com> wrote:

I think n is always non-negative in the trig series, but some Laurent
series use negative n values. So (-1)^^n might be useful for negative n,
but you could always rewrite it as (n%2 ? -1 : 1) or ~(n&1)+1.

Oops, ~(n&1)+1 doesn't work, but ~((n&1)<<1)+2 does.

--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
```
Dec 08 2009
Bill Baxter <wbaxter gmail.com> writes:
```On Tue, Dec 8, 2009 at 1:58 PM, Phil Deets <pjdeets2 gmail.com> wrote:
On Tue, 08 Dec 2009 16:44:46 -0500, Phil Deets <pjdeets2 gmail.com> wrote:

I think n is always non-negative in the trig series, but some Laurent
series use negative n values. So (-1)^^n might be useful for negative n, but
you could always rewrite it as (n%2 ? -1 : 1) or ~(n&1)+1.

Oops, ~(n&1)+1 doesn't work, but ~((n&1)<<1)+2 does.

Right, and you can always write x^^y as pow(x,y).  A major point of
opPow is to avoid unnatural wonky-isms like you what you wrote there.

--bb
```
Dec 08 2009
Don <nospam nospam.com> writes:
```Phil Deets wrote:
On Tue, 08 Dec 2009 12:42:33 -0500, Bill Baxter <wbaxter gmail.com> wrote:

On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
[snip]
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs,
regardless of the value of x. This error is detected at compile time, if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.  I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

time, is the result 0?

No, it's a compile-time error.
```
Dec 08 2009
"Phil Deets" <pjdeets2 gmail.com> writes:
```On Tue, 08 Dec 2009 22:26:10 -0500, Don <nospam nospam.com> wrote:

Phil Deets wrote:
On Tue, 08 Dec 2009 12:42:33 -0500, Bill Baxter <wbaxter gmail.com>
wrote:

On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
[snip]
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs,
regardless of the value of x. This error is detected at compile time,
if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.  I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

time, is the result 0?

No, it's a compile-time error.

It can't be a compile-time error if it can't be detected at compile time
like I said in my question. In the code

2^^SomeFunctionFromACModuleThatReturnsInt();

the compiler can't know whether the exponent will be positive or negative.
When the code runs and a negative result is returned, will the result be
zero?

--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
```
Dec 08 2009
"Phil Deets" <pjdeets2 gmail.com> writes:
```On Wed, 09 Dec 2009 02:40:44 -0500, Phil Deets <pjdeets2 gmail.com> wrote:

On Tue, 08 Dec 2009 22:26:10 -0500, Don <nospam nospam.com> wrote:

Phil Deets wrote:
On Tue, 08 Dec 2009 12:42:33 -0500, Bill Baxter <wbaxter gmail.com>
wrote:

On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
[snip]
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs,
regardless of the value of x. This error is detected at compile
time, if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.  I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

time, is the result 0?

No, it's a compile-time error.

It can't be a compile-time error if it can't be detected at compile time
like I said in my question. In the code

2^^SomeFunctionFromACModuleThatReturnsInt();

the compiler can't know whether the exponent will be positive or
negative. When the code runs and a negative result is returned, will the
result be zero?

Maybe you meant, if you can't prove it's positive, it's an error. If so, I
would suggest requiring an unsigned type.

--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
```
Dec 08 2009
Don <nospam nospam.com> writes:
```Phil Deets wrote:
On Wed, 09 Dec 2009 02:40:44 -0500, Phil Deets <pjdeets2 gmail.com> wrote:

On Tue, 08 Dec 2009 22:26:10 -0500, Don <nospam nospam.com> wrote:

Phil Deets wrote:
On Tue, 08 Dec 2009 12:42:33 -0500, Bill Baxter <wbaxter gmail.com>
wrote:

On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
[snip]
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs,
regardless of the value of x. This error is detected at compile
time, if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.  I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

time, is the result 0?

No, it's a compile-time error.

It can't be a compile-time error if it can't be detected at compile
time like I said in my question. In the code

Sorry. I misread that. I wrote that at 3am.
2^^SomeFunctionFromACModuleThatReturnsInt();

the compiler can't know whether the exponent will be positive or
negative. When the code runs and a negative result is returned, will
the result be zero?

Maybe you meant, if you can't prove it's positive, it's an error. If so,
I would suggest requiring an unsigned type.

Read the proposal. It's a runtime divide error.
```
Dec 08 2009
Don <nospam nospam.com> writes:
```Bill Baxter wrote:
On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

Is that consistent with math?   I think in math they usually write
(-1)^n with parens.
See for example the sin power series here:
http://en.wikipedia.org/wiki/Power_series

Hmm, that's what's

What's the rationale for going against math here?

* The type of x ^^ y is the same as the type of x * y.

Like it.

* If y == 0,  x ^^ y is 1.

Need to mention what happens when x is also 0.

No.  0^^0 is 1, as well.

* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error occurs,
regardless of the value of x. This error is detected at compile time, if
possible.

Can you explain why you think that's necessary?  Seems like going too
far towards a nanny compiler for no particularly good reason.

The fact that 2^^-1 isn't particularly useful doesn't make it
particularly error prone.  No more so than integer division when the
person meant floating point division.

Integer division is a well defined operation. 2^^-1 is *never* sensible.

I just find it unexpected that
a language would single out exponentiation for this kind of treatment.

* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

Hmm, I'm not so sure about that.  I saw examples of this being used
even in the small sampling of search results from Python and Fortran
code that I looked at.  Many mathematical constructs are defined as
having a leading sign of (-1)^^n  (like the sin series formula linked
above).

Yes, but n is always positive in those formulae.

In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the compiler to
use range propagation on the value of y to detect most exponentiation errors
at compile time. (If those cases are legal, the compiler can't generate an
error on x^^-2, because of the possibility that x might be 1 or -1).

I didn't see this error as adding much value when there was nothing
clearly lost from it.  But here you're showing there is a real price
to pay for this nanny compiler behavior.   To me that makes the error
clearly not worth the price of admission.  I also think that since 0^0

Rubbish. 0^^0 is 1.

and 0^-1 and such are mathematically undefined, careful users of opPow
will already have to put some if() check before blindly doing an x^^y.
if(x!=0 || y!=0)  x^^y;
the people who care can just change the check to
if(x!=0 || y>0) x^^y;

Doesn't changing that one operator there doesn't seem like an undue
burden upon those who are careful checkers of their values.

Also note that making it an error leaves open the possibility of changing it
to a non-error later, without breaking code; but going from non-error to
error would be more difficult.

I think it's pretty clear that the error goes too far right now
without taking a wait-and-see stance.

Do you realize you are asking for ^^ to be removed? I'm not joking.
```
Dec 08 2009
Bill Baxter <wbaxter gmail.com> writes:
```On Tue, Dec 8, 2009 at 7:24 PM, Don <nospam nospam.com> wrote:
* If y =3D=3D 0, =A0x ^^ y is 1.

Need to mention what happens when x is also 0.

No. =A00^^0 is 1, as well.

Is it?  That's rather embarrassing for Wolfram Alpha, then (and
presumably Mathematica, too) since they have it as "indeterminate":
http://www.wolframalpha.com/input/?i=3D0^0

(1) Although the following special cases could be defined...
=A0* If x =3D=3D 1, =A0x ^^ y is 1
=A0* If x =3D=3D -1 and y is even, x^^y =3D=3D 1
=A0* If x =3D=3D -1 and y is odd, x^^y =3D=3D -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce.

Hmm, I'm not so sure about that. =A0I saw examples of this being used
even in the small sampling of search results from Python and Fortran
code that I looked at. =A0Many mathematical constructs are defined as
having a leading sign of (-1)^^n =A0(like the sin series formula linked
above).

Yes, but n is always positive in those formulae.

Well, non-negative actually.
But yeh, that is true as far as the ones I know, too.

I didn't see this error as adding much value when there was nothing
clearly lost from it. =A0But here you're showing there is a real price
to pay for this nanny compiler behavior. =A0 To me that makes the error
clearly not worth the price of admission. =A0I also think that since 0^0

Rubbish. 0^^0 is 1.

Yeh, again Wolfram steered me wrong there, if you correct about that.

and 0^-1 and such are mathematically undefined, careful users of opPow
will already have to put some if() check before blindly doing an x^^y.
=A0 if(x!=3D0 || y!=3D0) =A0x^^y;
the people who care can just change the check to
=A0 if(x!=3D0 || y>0) x^^y;

Doesn't changing that one operator there doesn't seem like an undue
burden upon those who are careful checkers of their values.

Also note that making it an error leaves open the possibility of changi=

ng
it
to a non-error later, without breaking code; but going from non-error t=

o
error would be more difficult.

I think it's pretty clear that the error goes too far right now
without taking a wait-and-see stance.

Do you realize you are asking for ^^ to be removed? I'm not joking. Walte=

r's

Walter's ready to pull it if you don't make negative powers on
integers an error?
Nope, didn't realize that.

Anyway, I've made my objections clear I think, so I won't reiterate.
Basically I just want to KISS, and be consistent with how / works, and
I think you guys get that but reject it.  Ok.

--bb
```
Dec 09 2009
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
```Bill Baxter wrote:
On Tue, Dec 8, 2009 at 7:24 PM, Don <nospam nospam.com> wrote:
* If y == 0,  x ^^ y is 1.

Need to mention what happens when x is also 0.

No.  0^^0 is 1, as well.

Is it?  That's rather embarrassing for Wolfram Alpha, then (and
presumably Mathematica, too) since they have it as "indeterminate":
http://www.wolframalpha.com/input/?i=0^0

It's not an either-or question. There are different definitions of 0^0,
and it's not obvious which is the "best one":

http://en.wikipedia.org/wiki/Exponentiation#Zero_to_the_zero_power

I think 0^^0 should give the same result as 0.0^^0.0, for which C has
set the precedent that pow(0.0, 0.0)==1.0.

-Lars
```
Dec 09 2009
Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
```Bill Baxter wrote:
On Tue, Dec 8, 2009 at 7:24 PM, Don <nospam nospam.com> wrote:
* If y == 0,  x ^^ y is 1.

Need to mention what happens when x is also 0.

No.  0^^0 is 1, as well.

Is it?  That's rather embarrassing for Wolfram Alpha, then (and
presumably Mathematica, too) since they have it as "indeterminate":
http://www.wolframalpha.com/input/?i=0^0

Confirmed with Mathematica.

In[2]:= 0^0
During evaluation of In[2]:= Power::indet: Indeterminate expression 0^0
encountered. >>
Out[2]= Indeterminate

-- Chris NS
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Bill Baxter wrote:
On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

Is that consistent with math?   I think in math they usually write
(-1)^n with parens.
See for example the sin power series here:
http://en.wikipedia.org/wiki/Power_series

What's the rationale for going against math here?

Hmm. Several other languages give it that precedence. But you're right,
it should be even higher than unary. Between unary and postfix ?
```
Dec 08 2009
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
```Don wrote:
Bill Baxter wrote:
On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:
Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

Is that consistent with math?   I think in math they usually write
(-1)^n with parens.
See for example the sin power series here:
http://en.wikipedia.org/wiki/Power_series

What's the rationale for going against math here?

Hmm. Several other languages give it that precedence. But you're right,
it should be even higher than unary. Between unary and postfix ?

Good catch, I missed that one in the original proposal. Between unary
and postfix sounds right. I would be extremely surprised if -2^^2
evaluated to 4, but I would have to think twice about i++^^2.

-Lars
```
Dec 09 2009
Don <nospam nospam.com> writes:
```CHANGES BASED ON FURTHER COMMENTS
--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1 : 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0,  x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution to
that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?
```
Dec 09 2009
Rainer Deyke <rainerd eldwood.com> writes:
```Don wrote:
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.

I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
behavior.  However, having different behavior if x is a compile-time
constant than if x is a variable is unacceptable because it silently
changes the (defined) behavior of a function when a runtime parameter is
changed to a template parameter or vice versa, or even when the compiler
becomes a bit more clever about CTFE.

--
Rainer Deyke - rainerd eldwood.com
```
Dec 09 2009
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
```Rainer Deyke wrote:
Don wrote:
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.

I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
behavior.  However, having different behavior if x is a compile-time
constant than if x is a variable is unacceptable because it silently
changes the (defined) behavior of a function when a runtime parameter is
changed to a template parameter or vice versa, or even when the compiler
becomes a bit more clever about CTFE.

I don't think it's a problem: Either it works as expected, or it causes
a compile-time error.

-Lars
```
Dec 09 2009
Rainer Deyke <rainerd eldwood.com> writes:
```Lars T. Kyllingstad wrote:
Rainer Deyke wrote:
I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
behavior.  However, having different behavior if x is a compile-time
constant than if x is a variable is unacceptable because it silently
changes the (defined) behavior of a function when a runtime parameter is
changed to a template parameter or vice versa, or even when the compiler
becomes a bit more clever about CTFE.

I don't think it's a problem: Either it works as expected, or it causes
a compile-time error.

Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

--
Rainer Deyke - rainerd eldwood.com
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Rainer Deyke wrote:
Rainer Deyke wrote:
I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
behavior.  However, having different behavior if x is a compile-time
constant than if x is a variable is unacceptable because it silently
changes the (defined) behavior of a function when a runtime parameter is
changed to a template parameter or vice versa, or even when the compiler
becomes a bit more clever about CTFE.

I don't think it's a problem: Either it works as expected, or it causes
a compile-time error.

Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

It won't pass CTFE.
```
Dec 09 2009
Rainer Deyke <rainerd eldwood.com> writes:
```Don wrote:
Rainer Deyke wrote:
Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

It won't pass CTFE.

pure int f() { return -1; }
void g(int)(int);
g!(f() ^^ f())(0); // Works.
g!(0)(f() ^^ f()); // Runtime error?

'f() ^^ f()' can be a compile-time constant, but isn't guaranteed to be
evaluated at compile time.

--
Rainer Deyke - rainerd eldwood.com
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Rainer Deyke wrote:
Don wrote:
Rainer Deyke wrote:
Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

It won't pass CTFE.

pure int f() { return -1; }
void g(int)(int);
g!(f() ^^ f())(0); // Works.

No, it doesn't work. It's exactly the same as:

int intpow(int x, int y)
{
assert(y>=0);
return x;
}

g!(intpow(f(), f())(0);

f() ^^ f() is not a constant expression. It won't get transformed. It
can be *interpreted* at compile time, but that'll generate an error.

g!(0)(f() ^^ f()); // Runtime error?

Definitely.

'f() ^^ f()' can be a compile-time constant, but isn't guaranteed to be
evaluated at compile time.

No, it's not a compile-time constant, unless it's turned into one.

If however you changed it to:

template eval(int x) {  enum int eval = x; }

g!( eval!(f()) ^^ f() )(0);

which turns f() into a compile-time constant, then it'd work.
But then it'd work at runtime, as well.
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Don wrote:
Rainer Deyke wrote:
Don wrote:
Rainer Deyke wrote:
Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

It won't pass CTFE.

pure int f() { return -1; }
void g(int)(int);
g!(f() ^^ f())(0); // Works.

No, it doesn't work. It's exactly the same as:

int intpow(int x, int y)
{
assert(y>=0);
return x;
}

g!(intpow(f(), f())(0);

f() ^^ f() is not a constant expression. It won't get transformed. It
can be *interpreted* at compile time, but that'll generate an error.

g!(0)(f() ^^ f()); // Runtime error?

Definitely.
'f() ^^ f()' can be a compile-time constant, but isn't guaranteed to be
evaluated at compile time.

No, it's not a compile-time constant, unless it's turned into one.

If however you changed it to:

template eval(int x) {  enum int eval = x; }

g!( eval!(f()) ^^ f() )(0);

which turns f() into a compile-time constant, then it'd work.
But then it'd work at runtime, as well.

You can see this effect in action in the current version of DMD, because
at present sqrt() can be evaluated at compile time, but pow() cannot be.
(this behaviour will be fixed, but for now it illustrates the point).

-----------

import std.math;

int bar(double x)() { return 0; }

double foo() { return 0.5; }

void main()
{
int z = bar!( 7.0 ^^ 0.5 )();  // works -- 0.5 is a constant
int z2 = bar!( 7.0 ^^ foo() )(); // fails -- foo() is not.
}
```
Dec 09 2009
Rainer Deyke <rainerd eldwood.com> writes:
```Don wrote:
f() ^^ f() is not a constant expression. It won't get transformed. It
can be *interpreted* at compile time, but that'll generate an error.

That's a very fine distinction.  One that may not survive future
evolution of the D language, and may not respected by other
implementations of the D language.  (I /should/ be removed.  Having
different rules for operators and functions unnecessarily complicates
the language.)  Code that depends on this distinction is highly fragile.
It should not be possible to write code that depends on this distinction.

--
Rainer Deyke - rainerd eldwood.com
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Rainer Deyke wrote:
Don wrote:
f() ^^ f() is not a constant expression. It won't get transformed. It
can be *interpreted* at compile time, but that'll generate an error.

That's a very fine distinction.

Yes. This is a very narrow special case. Bill Baxter and others have
argued that it is such an important one, that it needs to be allowed.
But it's surrounded by nasty stuff which definitely must not be allowed.

One that may not survive future
evolution of the D language, and may not respected by other
implementations of the D language.

I think you're confusing 'pure' with 'constant expression'. They are not
the same thing.

To repeat what I said in the previous post:
f() is *not* a compile-time constant. Never. But you can use it to make one.

(I /should/ be removed.  Having
different rules for operators and functions unnecessarily complicates
the language.)

Do you mean the fact that constant folding always happens for operators,
but that CTFE doesn't happen automatically?

Code that depends on this distinction is highly fragile.
It should not be possible to write code that depends on this distinction.

How can you write code that depends on this distinction?
```
Dec 09 2009
Rainer Deyke <rainerd eldwood.com> writes:
```Don wrote:
Rainer Deyke wrote:
One that may not survive future
evolution of the D language, and may not respected by other
implementations of the D language.

I think you're confusing 'pure' with 'constant expression'. They are not
the same thing.

No.

(I /should/ be removed.  Having
different rules for operators and functions unnecessarily complicates
the language.)

Do you mean the fact that constant folding always happens for operators,
but that CTFE doesn't happen automatically?

Yes.

Code that depends on this distinction is highly fragile.
It should not be possible to write code that depends on this
distinction.

How can you write code that depends on this distinction?

Thinking about it again, this may not be as much of a problem as I have
been saying.  If -1 ^^ -1 works for compile-time constants, then
expanding the definition of compile-time constant is unlikely to break
any code.

--
Rainer Deyke - rainerd eldwood.com
```
Dec 09 2009
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
```Rainer Deyke wrote:
Rainer Deyke wrote:
I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
behavior.  However, having different behavior if x is a compile-time
constant than if x is a variable is unacceptable because it silently
changes the (defined) behavior of a function when a runtime parameter is
changed to a template parameter or vice versa, or even when the compiler
becomes a bit more clever about CTFE.

I don't think it's a problem: Either it works as expected, or it causes
a compile-time error.

Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

That depends on which of these two rules gets the higher precedence:

Don wrote:
* If x is the compile-time constant -1 and y is an integer, x^^y is rewritten
as (y & 1) ? -1 : 1.
* If y < 0, an integer divide error occurs, regardless of the value of x.

I suppose it should be the latter.

-Lars
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Rainer Deyke wrote:
Rainer Deyke wrote:
I don't care if x^^y with y < 0 is 0, a runtime error, or even undefined
behavior.  However, having different behavior if x is a compile-time
constant than if x is a variable is unacceptable because it silently
changes the (defined) behavior of a function when a runtime parameter is
changed to a template parameter or vice versa, or even when the compiler
becomes a bit more clever about CTFE.

I don't think it's a problem: Either it works as expected, or it causes
a compile-time error.

Not quite.  Under the proposal, -1^^-1 works (i.e. produces the correct
result) at compile time but fails at runtime.

No. That's why I wrote it as a transformation rule. CTFE is not
permitted to perform transformations, it can only evaluate.
```
Dec 09 2009
"Simen kjaeraas" <simen.kjaras gmail.com> writes:
```On Wed, 09 Dec 2009 09:36:56 +0100, Don <nospam nospam.com> wrote:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1
: 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0,  x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value of
x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises
like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution to
that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

Might I enquire as to why the exponent should be allowed to be signed
for integer exponentiation? It's been covered several times - it makes
no sense.

Apart from that - great job!

--
Simen
```
Dec 09 2009
KennyTM~ <kennytm gmail.com> writes:
```On Dec 9, 09 17:25, Simen kjaeraas wrote:
On Wed, 09 Dec 2009 09:36:56 +0100, Don <nospam nospam.com> wrote:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1 : 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0, x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises
like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution
to that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

Might I enquire as to why the exponent should be allowed to be signed
for integer exponentiation? It's been covered several times - it makes
no sense.

Apart from that - great job!

Because 3^^-1 would become 3^^4294967295 (note: int can be implicitly
converted to uint) which then you spend 17 seconds to get 2863311531 and
wonder what's going on.
```
Dec 09 2009
bearophile <bearophileHUGS lycos.com> writes:
```KennyTM~:
Because 3^^-1 would become 3^^4294967295

I agree, no unsigned values, please.
In D unsigned values are bug-prone, it's better to avoid them when possible,
they are useful almost only when you need a bitfield (they are useful for
arithmetic only exceptionally, when you really need a positive range larger
than the signed one and you can't use a value with more bits).

Bye,
bearophile
```
Dec 09 2009
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```KennyTM~ wrote:
On Dec 9, 09 17:25, Simen kjaeraas wrote:
On Wed, 09 Dec 2009 09:36:56 +0100, Don <nospam nospam.com> wrote:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1
: 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0, x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value
of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises
like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution
to that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

Might I enquire as to why the exponent should be allowed to be signed
for integer exponentiation? It's been covered several times - it makes
no sense.

Apart from that - great job!

Because 3^^-1 would become 3^^4294967295 (note: int can be implicitly
converted to uint) which then you spend 17 seconds to get 2863311531 and
wonder what's going on.

On a non-degenerate base, any exponent greater than 32 (for int) and 64
(for long) would generate overflow. That can be figured with one test.

That's why I'm saying the exponential grows fast. The range of useful
exponents is extremely limited.

Andrei
```
Dec 09 2009
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```Andrei Alexandrescu wrote:
On a non-degenerate base, any exponent greater than 32 (for int) and 64
(for long) would generate overflow. That can be figured with one test.

In fact probably all exponents can be special-cased to use the minimal
amount of multiplications (which in the general case is not easy to figure).

Andrei
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Simen kjaeraas wrote:
On Wed, 09 Dec 2009 09:36:56 +0100, Don <nospam nospam.com> wrote:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)?
1 : 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0,  x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value
of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises
like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution
to that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

Might I enquire as to why the exponent should be allowed to be signed
for integer exponentiation? It's been covered several times - it makes
no sense.

Because I think it would be too restrictive. Consider code like this:

for (int n=0; n<10; ++n)
{
v[n] = x ^^ n;
}

Requiring it to be a uint would be OK if the compiler's range
propagation was near-perfect, but I don't think that's going to happen.

Apart from that - great job!

```
Dec 09 2009
KennyTM~ <kennytm gmail.com> writes:
```On Dec 9, 09 16:36, Don wrote:
--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1 : 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0, x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution to
that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

0^^-1 == 0 ?
```
Dec 09 2009
Don <nospam nospam.com> writes:
```KennyTM~ wrote:
On Dec 9, 09 16:36, Don wrote:
--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1 : 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0, x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises
like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution to
that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

0^^-1 == 0 ?

Good catch. That line is completely wrong.
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Don wrote:
--------------------
x ^^ y is right associative, and has a precedence intermediate between
unary and postfix operators.
The type of x ^^ y is the same as the type of x * y.

* If either x or y are floating-point, the result is pow(x, y).

If both x and y are integers, the following rules apply:

* If x is the compile-time constant 0, x^^y is rewritten as (y==0)? 1 : 0
* If x is the compile-time constant 1, x^^y is rewritten as (y,1)
* If x is the compile-time constant -1 and y is an integer, x^^y is
rewritten as (y & 1) ? -1 : 1.

* If y == 0, x ^^ y is 1.
* If y > 0,  x ^^ y is functionally equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If y < 0, an integer divide error occurs, regardless of the value of x.

-----------
Note that by definining the 0,1, -1 cases as "rewriting" rules rather
than return values, it should be clearer that they don't apply to
variables having those values.
I think this covers everything useful, while avoiding nasty surprises like

double y = x ^^ -1; // looks like reciprocal, but isn't!
// Yes, this IS the same problem you get with double y = 1/x.
// But that's doesn't make it acceptable. I have a possible solution to
that one, too.

I don't think we can afford to spend much more time on this.
Is everyone happy now?

Stuff it, it's too hard to explain.
No negative exponents.
Anyone who wants (-1) ^^ n where n is negative, will have to write
(-1) ^^ abs(n). Sorry, Bill. It ain't worth the complexity just to save
5 characters of syntax sugar.
```
Dec 09 2009
Jason House <jason.james.house gmail.com> writes:
```I think you have a bad corner case:

enum int ct = -1;
immutable rt = -1;

ct ^^ ct // Error (compile time)
rt ^^ ct // Error (compile time)
rt ^^ rt // Error (run time)
ct ^^ rt // Works??? (after rewrite)

Don Wrote:

Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at compile
time, if possible.
* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce. In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the
compiler to use range propagation on the value of y to detect most
exponentiation errors at compile time. (If those cases are legal, the
compiler can't generate an error on x^^-2, because of the possibility
that x might be 1 or -1).
Also note that making it an error leaves open the possibility of
changing it to a non-error later, without breaking code; but going from
non-error to error would be more difficult.

(2) USE OF THE INTEGER DIVIDE ERROR
Note that on x86 at least, a hardware "integer divide error", although
commonly referred to as "division by zero", also occurs when the DIV
instruction, which performs uint = ulong/uint, results in a value
greater than uint.max. Raising a number to a negative power does involve
a division, so it seems to me not unreasonable to use it for this case
as well.
Note that 0 ^^ -1 is a division by zero.
This means that, just as you should check that y!=0 before performing
x/y, you should check that y>=0 before performing x^^y.

(3) OVERFLOW
int ^^ int returns an int, not a long. Although a long would allow
representation of larger numbers, even doubling the number of bits
doesn't help much in avoiding overflow, because x^^y is exponential.
Even a floating-point representation can easily overflow:
5000^5000 easily overflows an 80-bit real.
So, it's preferable to retain the simplicity that typeof(x^^y) is
typeof(x*y).

```
Dec 09 2009
Jason House <jason.james.house gmail.com> writes:
```oops, my message was supposed to be in reply to version 3 that included the
rewrite rule for -1 ^^ y

Jason House Wrote:

I think you have a bad corner case:

enum int ct = -1;
immutable rt = -1;

ct ^^ ct // Error (compile time)
rt ^^ ct // Error (compile time)
rt ^^ rt // Error (run time)
ct ^^ rt // Works??? (after rewrite)

Don Wrote:

Based on everyone's comments, this is what I have come up with:

--------------------
x ^^ y is right associative, and has a precedence intermediate between
multiplication and unary operators.

* The type of x ^^ y is the same as the type of x * y.
* If y == 0,  x ^^ y is 1.
* If both x and y are integers, and y > 0,  x^^y is equivalent to
{ auto u = x; foreach(i; 1..y) { u *= x; } return u; }
* If both x and y are integers, and y < 0, an integer divide error
occurs, regardless of the value of x. This error is detected at compile
time, if possible.
* If either x or y are floating-point, the result is pow(x, y).
--------------------
Rationale:
(1) Although the following special cases could be defined...
* If x == 1,  x ^^ y is 1
* If x == -1 and y is even, x^^y == 1
* If x == -1 and y is odd, x^^y == -1
... they are not sufficiently useful to justify the major increase in
complexity which they introduce. In all other cases, a negative exponent
indicates an error; it should be rewritten as (cast(real)x) ^^ y. Making
these cases errors makes everything much simpler, and allows the
compiler to use range propagation on the value of y to detect most
exponentiation errors at compile time. (If those cases are legal, the
compiler can't generate an error on x^^-2, because of the possibility
that x might be 1 or -1).
Also note that making it an error leaves open the possibility of
changing it to a non-error later, without breaking code; but going from
non-error to error would be more difficult.

(2) USE OF THE INTEGER DIVIDE ERROR
Note that on x86 at least, a hardware "integer divide error", although
commonly referred to as "division by zero", also occurs when the DIV
instruction, which performs uint = ulong/uint, results in a value
greater than uint.max. Raising a number to a negative power does involve
a division, so it seems to me not unreasonable to use it for this case
as well.
Note that 0 ^^ -1 is a division by zero.
This means that, just as you should check that y!=0 before performing
x/y, you should check that y>=0 before performing x^^y.

(3) OVERFLOW
int ^^ int returns an int, not a long. Although a long would allow
representation of larger numbers, even doubling the number of bits
doesn't help much in avoiding overflow, because x^^y is exponential.
Even a floating-point representation can easily overflow:
5000^5000 easily overflows an 80-bit real.
So, it's preferable to retain the simplicity that typeof(x^^y) is
typeof(x*y).

```
Dec 09 2009
Don <nospam nospam.com> writes:
```Jason House wrote:
I think you have a bad corner case:

enum int ct = -1;
immutable rt = -1;

ct ^^ ct // Error (compile time)
rt ^^ ct // Error (compile time)
rt ^^ rt // Error (run time)
ct ^^ rt // Works??? (after rewrite)

No, they will all work. Both rt and ct are manifest constants.
```
Dec 09 2009
Don <nospam nospam.com> writes:
```Don wrote:
Jason House wrote:
I think you have a bad corner case:

enum int ct = -1;
immutable rt = -1;

ct ^^ ct // Error (compile time)

This would always get rewritten.

rt ^^ ct // Error (compile time)
rt ^^ rt // Error (run time)
ct ^^ rt // Works??? (after rewrite)

No, they will all work. Both rt and ct are manifest constants.

(Actually it's a bit of wierd compiler quirk that const/immutables
initialized with literal values are manifest constants; they aren't
constants if initialized with anything else. This behaviour may change,
but it doesn't affect this scheme).

If however rt is just

int rt = -1;

then

ct ^^ ct and ct ^^ rt work, and the other two fail. This is intended.
```
Dec 09 2009