## digitalmars.D - Semantics of ^^

- Don <nospam nospam.com> Dec 08 2009
- "Denis Koroskin" <2korden gmail.com> Dec 08 2009
- "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> Dec 08 2009
- "Steven Schveighoffer" <schveiguy yahoo.com> Dec 08 2009
- Don <nospam nospam.com> Dec 08 2009
- "Steven Schveighoffer" <schveiguy yahoo.com> Dec 08 2009
- Bill Baxter <wbaxter gmail.com> Dec 08 2009
- Don <nospam nospam.com> Dec 08 2009
- "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> Dec 09 2009
- Chris Nicholson-Sauls <ibisbasenji gmail.com> Dec 09 2009
- Don <nospam nospam.com> Dec 08 2009
- Don <nospam nospam.com> Dec 08 2009
- Don <nospam nospam.com> Dec 08 2009
- "Phil Deets" <pjdeets2 gmail.com> Dec 08 2009
- "Phil Deets" <pjdeets2 gmail.com> Dec 08 2009
- Bill Baxter <wbaxter gmail.com> Dec 08 2009
- "Phil Deets" <pjdeets2 gmail.com> Dec 08 2009
- "Phil Deets" <pjdeets2 gmail.com> Dec 08 2009
- Don <nospam nospam.com> Dec 09 2009
- Rainer Deyke <rainerd eldwood.com> Dec 09 2009
- "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> Dec 09 2009
- Rainer Deyke <rainerd eldwood.com> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- Rainer Deyke <rainerd eldwood.com> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- Rainer Deyke <rainerd eldwood.com> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- Rainer Deyke <rainerd eldwood.com> Dec 09 2009
- "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- "Simen kjaeraas" <simen.kjaras gmail.com> Dec 09 2009
- KennyTM~ <kennytm gmail.com> Dec 09 2009
- bearophile <bearophileHUGS lycos.com> Dec 09 2009
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- KennyTM~ <kennytm gmail.com> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- Don <nospam nospam.com> Dec 09 2009
- Bill Baxter <wbaxter gmail.com> Dec 09 2009

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

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

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 course overload ^^) if they are worried about this. In any case, the 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

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

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. Google for "addition chains" if you're interested in the optimal 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

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).

<< y

And if x is 4, it becomes 1 << 2*y, etc. Google for "addition chains" if you're interested in the optimal 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

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=

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

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

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. Instead of 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=

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

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'sWhat'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 thata 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. Instead of 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. Walter's ready to pull it out. Please reconsider.

Dec 08 2009

Bill Baxter wrote:On Tue, Dec 8, 2009 at 7:24 PM, Don <nospam nospam.com> wrote:* If y == 0, x ^^ y is 1.

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

Bill Baxter wrote:On Tue, Dec 8, 2009 at 7:24 PM, Don <nospam nospam.com> wrote:* If y == 0, x ^^ y is 1.

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

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.

I was also wondering about this. If it can't be detected at compile time, is the result 0?

No, it's a compile-time error.

Dec 08 2009

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

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.

Dec 08 2009

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

Don wrote:Bill Baxter wrote:On Tue, Dec 8, 2009 at 2:32 AM, Don <nospam nospam.com> wrote:

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

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]

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.

I was also wondering about this. If it can't be detected at compile time, 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

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

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

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:

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

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:

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

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

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

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

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

Rainer Deyke wrote: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.

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

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

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.

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 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.

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

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

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 futureevolution 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. Havingdifferent 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

Don wrote:Rainer Deyke wrote: One that may not survive futureevolution 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. Havingdifferent 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

Rainer Deyke wrote: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.

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

Rainer Deyke wrote:Lars T. Kyllingstad wrote:Rainer Deyke wrote:

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.

permitted to perform transformations, it can only evaluate.

Dec 09 2009

On Wed, 09 Dec 2009 09:36:56 +0100, Don <nospam nospam.com> wrote: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?

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

On Dec 9, 09 17:25, Simen kjaeraas wrote:On Wed, 09 Dec 2009 09:36:56 +0100, Don <nospam nospam.com> wrote: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?

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

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

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:

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 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

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

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

On Dec 9, 09 16:36, Don wrote:

0^^-1 == 0 ?

Dec 09 2009

KennyTM~ wrote:On Dec 9, 09 16:36, Don wrote:

0^^-1 == 0 ?

Good catch. That line is completely wrong.

Dec 09 2009

Don wrote:

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

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. =A0Instead of =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=

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

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=

ready to pull it out. Please reconsider.

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