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

Reply via email to