Describe the project you are working on:
A game
Describe the problem or limitation you are having in your project:
There is no exponentiation operator in GDScript. There is only the pow() function, which is a bit inconvenient.
Describe the feature / enhancement and how it helps to overcome the problem or limitation:
A ** B does not add extra parenthesesA ** B little shorter than pow(A, B)Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
A ** B == C, where A, B, C are float, C == pow(A, B)
If this enhancement will not be used often, can it be worked around with a few lines of script?:
No, you cannot add your own operators to GDScript.
Is there a reason why this should be core and not an add-on in the asset library?:
Yes. This is available in many programming languages, for example, JavaScript, Python.
Note that according to a discussion I had on IRC with @Zylann, pow() is significanty slower compared to unrolling the multiplication like this: A * A, B * B * B, ...
Of course, you can only do this with integer exponents, but this aspect should be kept in mind. Maybe the exponentiation operator can avoid the slowdown when using an integer exponent.
I don't think it should always unroll operands though. If they are function calls, randoms or expensive expressions, they should not be run multiple times.
I am not a specialist in the development of parsers and compilers, but the following optimizations come to my mind:
const POS_INT_CONST = 5
const NEG_INT_CONST = -5
var int_var: int
var float_var: float
var any_var
func f():
var a = x ** POS_INT_CONST # Multiply several times
var b = x ** NEG_INT_CONST # Multiply several times and find the inverse number
var c = x ** int_var # Check the sign, then #a or #b
var d = x ** float_var # Check if it is an integer, then #c or pow()
var e = x ** any_var # Check the type, then #d
In any case, other programming languages (even with dynamic typing) somehow cope with this problem.
If the operand is neither a variable nor a constant, I guess it just evaluates it, pushes the result on the stack and exponents that instead.
If the operand is neither a variable nor a constant
Any (even complex) expression has a forward known static type (in the worst case, it's Variant). len(x) will always return int value regardless of the type of expression x. But yes, as far as I know, now the compiler does not use type signatures for optimization.
I was talking about the left-hand operand
I was talking about the left-hand operand
It also has a static type. That is, it is possible to predict whether the left operand will be int or float. What is the problem?
The left operand's type doesnt really matter regarding what I originally commented about.
I was just saying we should make sure it's not evaluated more than once, as it happened in the past with another operator (+= I think it was, I can't find the issue link).
To put it back in context with Calinou's comment, in an unrollable scenario, B ** 3 should not be converted into B * B * B, but instead B should be placed in a temporary variable b on the stack, and then do b * b * b.
If this enhancement will not be used often, can it be worked around with a few lines of script?:
No, you cannot add your own operators to GDScript.
Well, you can still just use pow().
In any case, other programming languages (even with dynamic typing) somehow cope with this problem.
Pretty sure they don't optimize that. If you can get away with compile time checks (when exponent is constant) then you could do it, but runtime checks defeats the purpose of the optimization (consider the case when none of the powers are possible to unroll). Especially because most people doing pow(x, 2) will just write x * x instead.
Python, for instance, have operator overloading so this optimization cannot even be considered. Curiously, I found this benchmark: https://chrissardegna.com/blog/python-expontentiation-performance/ which concludes that calling math.pow() is faster than using the ** operator.
I was looking for a way to "unpack" ("destructuring) a Dict in GDscript. Seems like Python has a ** operator, e.g:
# A Python program to demonstrate packing of
# dictionary items using **
def fun(**kwargs):
# kwargs is a dict
print(type(kwargs))
# Printing dictionary items
for key in kwargs:
print("%s = %s" % (key, kwargs[key]))
# Driver code
fun(name="geeks", ID="101", language="Python")
Output:
<class 'dict'>
language = Python
name = geeks
ID = 101
This seems like a better use for **. It would avoid the issue of which arg to pass at which position when calling a function that accepts several arguments.
This seems like a better use for
**.
Firstly, nothing prevents both operators from using ** syntax. The exponentiation operator is binary infix, while the unpack operator is unary prefix. In Python, the exponentiation operator has exactly the ** syntax:
$ python -q
>>> 2 ** 3
8
Secondly, perhaps the ... syntax is better for these purposes. See #1034.
@dalexeev fair enough and yes I prefer the rest (...) syntax though probably because I'm more familiar with JS than Python. Latter has * and ** for this (tuple v.s. dict) - from what I can tell after a quick google search... no tuples in GDScript and it's just cleaner to have one syntax.
In any case, what I was originally looking for was something like this:
func tmp({ arg1 = 1, arg2 = 2 } = {}):
# ...
That would allow me to access arg1 directly rather than kwargs['arg1']. Also allows for default values.
Re ** as shorthand for pow(), if latter is faster I think it's not the best idea to encourage users to use former by making a shorthand for it (when it's not even the same thing - performance wise).
Re
**as shorthand forpow(), if latter is faster I think it's not the best idea to encourage users to use former by making a shorthand for it (when it's not even the same thing - performance wise).
See discussion above. With proper implementation, x ** 3 will be even faster than pow(x, 3).
@dalexeev - right I didn't ready the whole thread. Well in that case, no objections :)
Do decision makers have any decision on this? :smiley:
_This repository needs "Approved" label or something similar._
Most helpful comment
The left operand's type doesnt really matter regarding what I originally commented about.
I was just saying we should make sure it's not evaluated more than once, as it happened in the past with another operator (
+=I think it was, I can't find the issue link).To put it back in context with Calinou's comment, in an unrollable scenario,
B ** 3should not be converted intoB * B * B, but insteadBshould be placed in a temporary variablebon the stack, and then dob * b * b.