csharplang
csharplang copied to clipboard
Proposal: Exponentiation operator
Consider adding an exponentiation operator that is higher in precedence than the multiplicative operators but lower in precedence than the unary operators.
In the Symbolism computer algebra library, +
, *
, /
, and -
are overloaded to create Sum
, Product
, Quotient
, and Difference
objects respectively. ^
is also overloaded to create Power
objects. However ^
, being the logical XOR operator, is lower in precedence than the multiplicative operators. Thus parenthesis must be used in expressions like a + b * (c ^ e)
.
Exponentiation operator in some other languages:
Language | Operator |
---|---|
F# | ** |
VB.NET | ^ |
TypeScript | ** |
JavaScript | ** |
Python | ** |
Haskell | ^ , ^^ , ** |
OCaml | ** |
Swift | Not built-in but can be defined |
FORTRAN | ** |
Ada | ** |
Nim | ^ |
ALGOL 60 | ** |
APL | * |
Why not a ** b
to represent a ^ b
? Prevents problems and confusion with the XOR operator, and should be quickly apparent.
Is this really so much better than
using static System.Math;
var result = Pow(a, b);
Yes, VB has it so it makes translation easier, also makes mathematical expression easier to read but the second point could be debated.
Sent from my iPhone I apologize for any typos Siri might have made. (503) 803-6077
On Aug 18, 2015, at 11:31 AM, Neal Gafter [email protected] wrote:
Is this really so much better than
using static System.Math;
var result = Pow(a, b);
— Reply to this email directly or view it on GitHub.
Why not a ** b to represent a ^ b? Prevents problems and confusion with the XOR operator, and should be quickly apparent.
:+1:
Yes, a **
operator would be nice.
However, would it be possible to add this operator and not interfere with the dereference operator (*
)? I guess since the dereference operator works on pointers, which aren't used in exponentiation operations, this seems like it would be OK.
F# also uses **
. F# - Arithmetic Operators
@paul1956
VB's ^
operator literally just translates into a call to Math.Pow
so there is no additional benefit to porting between the languages. There is also nothing stipulating feature parity or direct porting of programs between the two languages and there are plenty of features between the two that cannot translate.
The second argument about math operations being easier to read is the more relevant argument in my opinion.
VB's ^
operator can be used in constants.
@LMLB 's argument is the most convincing to me- C# 7.0 is adding digit separators and binary literals for the "defining a constant" use case, and this seems at least as useful as either of those features.
I think more pertinent to this specific use case is that the operator can be overloaded whereas Math.Pow()
cannot.
@gafter
Is this really so much better than
using static System.Math; var result = Pow(a, b);
It is.
Exponentiation is no different from addition or multiplication: it is a common mathematical operation for which there is a universal notation (including precedence rules) that is approximated in programming languages.
C doesn't have an exponentiation operator, while it does have operators for closer to the metal operations like bit-wise xor (^
) and integer shifts. It seems java and C# inherited this design more or less by default without fully considering its merits. Most modern languages that are more oriented towards user-experience (Python, VB, R...) do have an exponentiation operator.
Furthermore, while operators are in scope and will be used when the defining type is in scope, this is not true for the static import in your example. It doesn't work within the scope of a type that itself defines a Pow
method, such as a complex number type or a vector type.
@HaloFour
VB's
^
operator literally just translates into a call toMath.Pow
Not exactly. There are some small differences. For example, the LINQ expression tree for the VB operator uses a binary expression of type Power
(where the Method
is Math.Pow
), while C#'s version uses a method call.
@gregsdennis It is possible to overload the VB operator from C# manually, i.e. by defining a static op_Exponent
method with a SpecialName
attribute. You can do the same for F#, but the operator method is called op_Exponentiation
(section 4.1 of the F# language spec). This is an unfortunate and annoying inconsistency.
It would be better if this was all consistent: have all common mathematical operations available as operators that can be overloaded in the same way and that makes them accessible to all .NET languages.
it is a common mathematical operation
Citation? :)
Furthermore, while operators are in scope and will be used when the defining type is in scope, this is not true for the static import in your example. It doesn't work within the scope of a type that itself defines a Pow method, such as a complex number type or a vector type.
If you're in the type itself, you don't need the static import. You can just directly call "Pow(x, y)"
If you're in the type itself, you don't need the static import. You can just directly call "Pow(x, y)"
But you can't access another type's Pow
method that is statically imported without qualifying.
In the code below, class A
's M
method is successfully called from class B
. However, in class C
, the presence of the M
method prevents it from being considered, unless you qualify it:
namespace N1
{
public class A
{
public static void M(string x) { }
}
}
namespace N2
{
using static N1.A;
public class B
{
public void M2()
{
M("Test"); // OK
}
}
public class C
{
public static void M(int x) { }
public void M2()
{
M(12); // OK
M("Test"); // 'Argument 1: Cannot convert from string to int'
N1.A.M("Test"); // OK
}
}
}
Citation? :)
This is like someone from Texas asking a Canadian for a citation that snow is a common weather phenomenon. :) Just because something isn't common in your experience doesn't mean it's not common for other people.
But since you asked: the code sample in the documentation for the static using
directive contains two squares, written out as multiplications.
Is exponentiation more common or important than square root? Where do you draw the line of which operations are important enough to deserve an operator?
The only benefit, IMHO, is that you can use mathematical operators statically. However, I think the constexpr feature (https://github.com/dotnet/roslyn/issues/15079) is a lot more general so I'd be in favor of that to fulfill that use case rather than adding a new operator.
Related #14665 but like @MgSam said please check #15079, if you like the idea join the discussion. :)
Just because something isn't common in your experience doesn't mean it's not common for other people.
And just because it's common for you, doesn't mean it's common enough to warrant inclusion in the language. :)
This is like someone from Texas asking a Canadian for a citation that snow is a common weather phenomenon
No. This is like a member of the LDM asking you what the basis of your claim is.
Claim: the scientific computing community needs better representation on the C# LDM. Citation: this thread.
This thread has 16 comments over an entire year of being open. There are only about 8 non-MS people even commenting on this. This seems to have very little interest (contrast with threads with dozens of people and hundreds of messages). Furthermore, the existence of interest does not mean that we should ,or will, take a request. We have literally hundreds to thousands of small requests like this. We definitely will not be taking the vast majority of them. We have to actually consider how important this is to the entirety of our ecosystem. Right now, the interest and impact of this feature are both low.
I have now seen that the comment i replied to has been deleted. So i'd like to nip this side of the conversation in the bud. My overall point is simply that this seems to be a feature without that much impact with very little interest.
--
From a technical perspective, i'm also worried about breaking changes here. Today, this is already legal code:
unsafe void X(int* p, int c)
{
var v = c**p
}
We'd likely have to do some sort of unpleasant contortions to ensure we didn't break existing code like this.
I agree that the syntactic ambiguity of **
is a large negative to the point where it's likely not worth the trouble.
D and Haskell use ^^
, which isn't great, but isn't terrible, either. It does not have any syntax issues that I'm aware of.
To give you an idea of how common the operation is, I looked at one of our projects, which is heavy on technical computing. About 0.9% of code lines contains an exponentiation. It is much more common than shifts or xors. Element-wise operations on arrays/collections are also quite common.
Although it is not primary evidence of commonality, the fact that the operator is included in many programming languages suggests that others found it sufficiently common to include in the language. Some of these languages had very tight constraints, like Altair BASIC, which fit in 8192 bytes (see manual (PDF), page 27).
Microsoft provides tooling for many languages that have the exponentiation operator:
- All Microsoft BASIC dialects, including VBScript, VBA, and VB.NET
- Microsoft F#
- Microsoft TypeScript 1.7 and ES7
- Microsoft Excel
- Python
- R
It's not clear to me that **
as an operator would cause an issue due to a conflict with the dereferencing operator.
Currently, in C#:
-
&
is used to obtain an address -
&
is used for logical AND -
&&
is conditional AND
There clearly aren't any implementation issues there.
Considering the example given above:
var v = c**p;
The above **
cannot be interpreted as a exponentiation operator because that would require treating p
as an exponent and p
is a pointer.
I'm fairly neutral regarding **
vs ^^
. However, **
seems more common. If there isn't a strong technical reason against **
, it seems like a good option.
It's not clear to me that ** as an operator would cause an issue due to a conflict with the dereferencing operator.
It would cause a syntactic ambiguity. Today we parse "a**b" as "a * (*b)" i.e. "a times the deref of b". With this new operator, we'd now parse that as "a ** b".
We do not change parse trees for correct code. So we would have to still parse things like the old way, but at semantics time, see if we had this pattern, and we'd have to determine what would be the right thing to do.
It would cause a syntactic ambiguity. Today we parse "a**b" as "a * (*b)" i.e. "a times the deref of b". With this new operator, we'd now parse that as "a ** b".
We do not change parse trees for correct code. So we would have to still parse things like the old way, but at semantics time, see if we had this pattern, and we'd have to determine what would be the right thing to do.
If the code isn't in unsafe
mode, then **
is unambiguously exponentiation.
If you are in unsafe
mode and the right operand of **
is a pointer, then dereference and multiply. Otherwise, exponentiation.
If you are in unsafe mode and the right operand of ** is a pointer, then dereference and multiply. Otherwise, exponentiation.
yes. this could be done. But now we have the same syntax meaning different things with operators. I'm not a fan of this. ++ doesn't mean something different depending on context. i.e. we don't say "oh, in a++b, there is no ++ operator for 'a', this this is 'a added to plus-b'".
It's not clear to me that ** as an operator would cause an issue due to a conflict with the dereferencing operator.
There is clearly a conflict here. The question is: is that acceptable? For example, there's a syntactic ambiguity with: (A)-B. That could be "minus B, casted to the A type". Or it should be "the value of parenthesized expr A, minus the value of B". We went with the former for C#. But once we decide on something, we don't change it**.
Here, we'd have to always parse this as "A times the deref of B". But we'd then have to have some special rule that says "if you have that, and there is no space between the operators, and B is derefable, then behave like so, otherwise, behave like so". That's pretty janky, and not something we've ever done before really.
--
** Ok, we also changed it for generic parsing: X(A<B, D>(E))
We changed the parsing here to make this into a nested invocation call, instead of two relational expressions. However, we're very wary of changing parsing. And i'm personally of hte opinion that it's very janky for us to special case the treatment of "a ** b" to processed differently depending on if 'b' is dereferencable or not.
@CyrusNajmabadi
it is a common mathematical operation
Citation? :)
API Port telemetry says that Math.Pow()
is used in 11 % of apps. Not sure if that is common enough.
@MgSam
Is exponentiation more common or important than square root? Where do you draw the line of which operations are important enough to deserve an operator?
The same data says that Math.Sqrt()
is used in 7 % of apps. So it is less common than exponentiation, but not by much. But while it's fairly easy to come up with decent operators for exponentiation (whether it's **
or ^^
), I don't see what would be a good operator for square root (I don't think √ a.k.a. U+221A SQUARE ROOT is an option).
API Port telemetry says that Math.Pow() is used in 11 % of apps. Not sure if that is common enough.
I'd want to know how often it was actually used. If 11% of all apps use the call once, then i have no problem with them using Math.Pow. :)
A sqrt operator would be superfluous. It's just x^^0.5
; a special case of <1 exponentiation.
A ** operator would be superfluous. It's just a call to Math.Pow. :)
An exponentiation operator is generic (general-use, not type generic), not a special case like a sqrt operator would be.
@dharmatech There is no ambiguity with && because && is always parsed as the short-circuit and operator. Same thing with ++ and --: c = a++b
is a syntax error because ++ is interpreted as the increment operator. It would have been a little bit harder to do for ** (because multiple derefs on one value are possible and meaningful), but it's simply too late now.
@gregsdennis There is decades of precedent for an exponentiation operator (FORTRAN-66 had it), but none for a square root operator. Not even APL has one.
@CyrusNajmabadi It actually gets even more complicated with precedence rules and the fact that pow is right-associative. For example, a * b ** c
is now parsed as (a * b) * (*c)
, but the correct interpretation with pow is a * (b ** c)
. So you can have a semantic tree that is very different, even structurally, from the syntax tree. You can't just say that the multiplication is really a power, and the deref is really a no-op: The arguments are different.
@svick Very interesting, thanks! That's probably a slight under-estimate because squares (and other small powers) are often optimized into multiplications. Math.Pow is 2 orders of magnitude slower than squaring by multiplying (but that's a different issue).