godot-proposals icon indicating copy to clipboard operation
godot-proposals copied to clipboard

Add an integer division operator to GDScript

Open daenvil opened this issue 9 months ago • 6 comments

Describe the project you are working on

Games and addons (not relevant, affects any project)

Describe the problem or limitation you are having in your project

Currently in Godot, you either enable or disable warnings for integer division. There's no direct way to purposefully use integer division but still be warned when you use it on accident.

Also Godot will still warn you even if you are using static typing, which makes no sense to me:

var x: int = 5
var y: int = 2
var z: int = x / y

This results in a warning, when I'm purposefully working with ints and clearly want an int as a result of the division.

If you work in a project with a few integer divisions, the current state of GDScript forces you to either disable warnings, clutter the code with warning_ignores, leave warnings unattended, or use floats in place of ints to avoid the warnings.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Add an integer division operator, equivalent to // in Python. This would allow to purposefully use integer division without resulting in warnings, but still be warned if you are accidentally using integer division with the regular division operator.

The regular division operator would keep working as it is.

Note that this is not the same as https://github.com/godotengine/godot-proposals/issues/1866, which was also proposing modifying the regular operator.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

E.g.:

var x: int = 5 // 2

would evaluate to 2 and not warn you about integer division.

The // operator would only allow ints as arguments.

If this enhancement will not be used often, can it be worked around with a few lines of script?

There's two current workarounds that I know of:

  • Using @warning_ignore("integer_division"): will ignore the warning, but you have to add it each time you are using integer division, which clutters the code a lot, and it's error-prone since it will ignore all divisions in the next line, which means that you won't get warned if you are using integer division accidentally in the same line (same but worse applies to @warning_ignore_start). It also requires more maintenance since you'll need to remember to remove that line if you remove the division that caused you to add the ignore. You can also remove the warnins altogether, but that's even more error-prone.
  • Using int(float(x) / y): will effectively bypass the warning since you are converting the int to float and then back to int, but it doesn't make sense to do two type conversions just for a basic operation like this, and it also clutters the code unnecessarily.

Is there a reason why this should be core and not an add-on in the asset library?

I believe this should be a core feature of GDScript. In any other language that I know you can use integer division with no warnings, either with a separate operator like proposed here or via strict typing like in C++.

daenvil avatar Mar 15 '25 08:03 daenvil

The problem is that the / operator remains confusing, and we can't change that in 4.x due to compatibility reasons. Having both / and // operators may lead users to expect that it works like in Python 3, when it doesn't.

Maybe we could introduce a global intdiv() function that always does integer division (even for floats, due to implicit conversions)? I can't say that this solves the issue with confusing / operator, but I think a global function instead of an operator better highlights the current state of Godot/GDScript. Also, we could better and more accessible (tooltips) document the global function, as opposed to the operator.

As for the INTEGER_DIVISION warning, I think its only purpose is to educate new users who haven't read the documentation carefully about the non-obvious division behavior. After that, the best solution is to disable the warning, as it is inconvenient in practice.

dalexeev avatar Mar 15 '25 14:03 dalexeev

The problem is that the / operator remains confusing, and we can't change that in 4.x due to compatibility reasons. Having both / and // operators may lead users to expect that it works like in Python 3, when it doesn't.

I don't think the / operator is confusing and I'm not proposing changing it. It does what it's supposed to do. I understand how it could be confusing for Python users, specially if we end up using the same two operators as Python, but I feel that since the integer_division warning would still be there for /, that should clear up any potential confusion.

As for the INTEGER_DIVISION warning, I think its only purpose is to educate new users who haven't read the documentation carefully about the non-obvious division behavior. After that, the best solution is to disable the warning, as it is inconvenient in practice.

I still see it as convenient since, even knowing how the division works, it's possible to accidentally use an int variable in a division without realizing it, and this warning helps you avoid those bugs entirely. If we had this explicit way of doing integer division there would be no reason to disable it.

daenvil avatar Mar 15 '25 15:03 daenvil

I would go the intdiv() route. Adding // while keeping /'s current behavior will only cause confusion for people coming over from Python. It doesn't follow the principle of least astonishment 🙂

Calinou avatar Mar 15 '25 17:03 Calinou

Also, there are two variants of integer division that give different results for negative numbers. For example, Haskell has div and quot operators (and mod and rem for modulo and remainder). Godot/GDScript has the % operator and the posmod() function. So maybe we should introduce not one, but two global functions for integer division. Or an operator and a global function, taking into account the modulo/remainder case.

dalexeev avatar Mar 16 '25 10:03 dalexeev

Why not ~/ like on Dart language?

lucasnlm avatar Mar 21 '25 01:03 lucasnlm

I did a quick test using ~/: https://github.com/godotengine/godot/pull/104487 What do you think?

Image

lucasnlm avatar Mar 22 '25 15:03 lucasnlm

Personally, I would prefer to change the behavior of the / operator to "real" division and introduce a separate operator(s) or global function(s) for integer division, as it is the most intuitive and expected option for most scripting languages. But unfortunately this cannot be done in 4.x for compatibility reasons.

This proposal makes sense to me if we decide to go in this direction, and change the behavior of the / operator in 5.0, thus making GDScript and Python more or less the same in this regard. (Note that Python's integer division and remainder are still different from GDScript's for negative numbers.)

But we have no plans for 5.x yet, and no clear understanding of the community's attitude towards this issue. So for now we are going by the current behavior of the division operator and the concern that the visual similarity to Python may increase confusion among users.

Choosing a different token than // might reduce the likelihood of confusion with Python, but I don't think it solves the main problem. The last time we discussed division behavior in Godot/GDScript, due to lack of consensus we decided against splitting the division operator into two operators, real and integer division. Introducing a separate operator for integer division now is somewhat inconsistent with that earlier decision. The / operator remains combined, we are not introducing two separate operators.

Also, ~/ doesn't seem like a very ergonomic option.

Overall, I would support introducing // for integer division (and possibly another operator for real division as a temporary solution for 4.x) if we want to change the behavior of / in 5.0. If we want to keep things as is, I think a global function like intdiv() would be preferable.

dalexeev avatar Mar 22 '25 16:03 dalexeev

Also, ~/ doesn't seem like a very ergonomic option.

WDYM? Why is it less ergo than, for example, two slashes?

Zireael07 avatar Mar 23 '25 10:03 Zireael07

Also, ~/ doesn't seem like a very ergonomic option.

WDYM? Why is it less ergo than, for example, two slashes?

I think it's because they want GDScript to be Python-like as much as possible. Less "ergo" is only valid in that context. For people that are not used to write python // is less ergo because // is used as comments in many languanges.

lucasnlm avatar Mar 23 '25 13:03 lucasnlm

Won't // mislead developers from C++ or C#, because of its different usages between languages like Cpp or CSharp and Python or GDScript?

Lazy-Rabbit-2001 avatar Mar 23 '25 13:03 Lazy-Rabbit-2001

I don't expect anyone to be confused by double slashes who are more than basically familiar with GDScript

I wouldn't expect ~/ to be an operator though, so it'd be harder to discover, it's also harder to type on US keyboards, as it's on the opposite side of the keyboard (and much harder on a number of layouts that have it as a dead key for example)

For example, on a Swedish layout this is a pretty complicated sequence, requiring you to press AltGr+¨ Shift+7, the first is a dead key which makes the procedure more clunky, on a UK layout (that I use for programming) it is Shift+# / where the first key is top right of the first, which isn't very smooth, and requires you to release shift part way through

AThousandShips avatar Mar 23 '25 13:03 AThousandShips

I don't expect anyone to be confused by double slashes who are more than basically familiar with GDScript

I wouldn't expect ~/ to be an operator though, so it'd be harder to discover, it's also harder to type on US keyboards, as it's on the opposite side of the keyboard (and much harder on a number of layouts that have it as a dead key for example)

For example, on a Swedish layout this is a pretty complicated sequence, requiring you to press AltGr+¨ Shift+7, the first is a dead key which makes the procedure more clunky, on a UK layout (that I use for programming) it is Shift+# / where the first key is top right of the first, which isn't very smooth, and requires you to release shift part way through

That's a good argument. I use a US keyboard, but ~ it's fine to me. I used to write Flutter, so ~/ is not a problem. But considering some layouts like you said are harder. It's a good point.

Anyway, that PR should work with // too. It's just a matter of update the parser. I'm not sure if I will have time to working on it.

lucasnlm avatar Mar 24 '25 01:03 lucasnlm

My main reason to create this proposal was because of the "integer_division" warnings. Coming from C++, integer division is a fundamental feature that is going to be used whenever someone works with static typing and ints. It's not the same as int(a/b), floor(a/b), or other similar workarounds, since those imply type casting from int to float and then back to int.

It doesn't make sense to me to force users to hide or ignore warnings when performing a basic operation like this, since I think warnings are there for a reason and shouldn't be hidden just for convenience, so that's why I thought adding a separate operator that enforces integer division and bypasses this warning would be a good option. If that operator or function is going to be equivalent to int(a/b) I don't see the point of it, though, in my view it should just take two ints, perform regular C++ division on them, and return the resulting int.

I also just found this comment on a related bug that sums up perfectly what I was trying to convey here.

Overall I agree that splitting them on real and integer division would be preferable, but that's out of the scope for 4.x, so I'm trying to find a compromise on what can actually be done to improve this on 4.x.

daenvil avatar Mar 24 '25 12:03 daenvil

From what I can see, the "unused variable" warning is similar in scope: It is completely valid to have unused variables, most of the times these are function parameters of functions that are overridden (e.g. _process). Here, the warning is silenced by explicitly prefixing the variable name with an underscore. Or more general, it is silenced by writing something explicitly at the point where the warning would be generated.

Transferring that to the integer division warning, it could be silenced by explicitly stating that the divisor is an integer like this: 4 / int(3). That would make it clear that you are dividing by an integer here.

A few more examples:

  • 4 / 3: Throws the warning like it does now.
  • 4 / three where var three: int = 3 also throws the warning, because types can be inferred from places in the code far away and reading this does not make it immediately visible.
  • 4 / int(three) would silence the warning.

Advantages:

  • No separate operator just for silencing a warning
  • Integer divisions are immediately visible at the exact places they are happening
  • The semantics of silencing this warning are similar to existing warnings
  • The extra int(...) conversion can be optimized away by GDScript if it knows the inner type is int already. (Maybe it already does this?)

timoschwarzer avatar Mar 24 '25 17:03 timoschwarzer

may be we can seperate '/' with '//' just with result, '/' always generate float, '//' always generate int. for negative '//', may be to ensure: (x//y)*y+(x%y)=x, this should be simple.

charlesw1234 avatar May 24 '25 13:05 charlesw1234

IMO the best choice for a integer div operator would be /:. It's easy to type (no dead keys), not confusing with comments, and the colon is often used to represent divisions and ratios, similar to how ÷ is used, but it's ASCII. So combining colon and slash (both associated with division) feels right for a div operator to me. A /: token shouldn't clash with anything (assuming you have no intention of introducing JS-style regex literals or other literals via a starting slash, or any syntax with a trailing slash).

/: can be introduced now in Godot 4. Whenever Godot 5 arrives, you might even change / to always do real division if you want, and still keep this warning when it's used in int/int because you can still choose to do explicit float(i)/int without efficiency loss I assume (to confirm it's float division), or switch to /: (to confirm you want int division).

But of course, if you go with a a.div(b) or divi(a, b) function, or even ~/or //, or suppressing it on constructs like explicit a/int(b) that's fine by me. But please let us do a normal expected basic math operation without having to fight against warnings, which is just not right. I shouldn't have to suppress anything, or resort to less efficient workarounds or custom functions.

And no, disabling the warning isn't a valid solution, because it's not just for noobs. I'm experienced, and that's why I WANT languages to be pedantic. I do want it to catch oversights I might make when dividing ints, I want to explicitly choose one way or the other, but I don't want to suppress anything as if I'm doing something wrong or not good for production. Specially for something this core to the language, and suppression makes the code extremely dirty.

geekley avatar Nov 11 '25 20:11 geekley

From what I can see, the "unused variable" warning is similar in scope: It is completely valid to have unused variables, most of the times these are function parameters of functions that are overridden (e.g. _process). Here, the warning is silenced by explicitly prefixing the variable name with an underscore. Or more general, it is silenced by writing something explicitly at the point where the warning would be generated.

Transferring that to the integer division warning, it could be silenced by explicitly stating that the divisor is an integer like this: 4 / int(3). That would make it clear that you are dividing by an integer here.

A few more examples:

  • 4 / 3: Throws the warning like it does now.
  • 4 / three where var three: int = 3 also throws the warning, because types can be inferred from places in the code far away and reading this does not make it immediately visible.
  • 4 / int(three) would silence the warning.

I really like this solution. It does not introduce any new, arcane operators and it clearly shows intent about what is supposed to happen. Also it's fairly clean and readable.

Also, if this gets changed again at some point in the future, well, so be it IMHO. It would be fairly easy to adjust your GDScript code.

Toxe avatar Nov 18 '25 10:11 Toxe

As I said in the proposal description, the main use case for this are situations like these:

var x: int = 5
var y: int = 2
var z: int = x / y

This would not be solved by the syntax proposed in https://github.com/godotengine/godot-proposals/issues/11998#issuecomment-2748860647 . Also, why would one need to specify x / int(y) when y is already an int? Why put int() in the denominator but not in the nominator? I'm sorry but it makes no sense. The comparison with the "unused variable" warning also misses the point as the purpose of this is not to "just silence a warning" but to provide a direct way of consistently performing a basic mathematical operation that we have in many other languages.

IMO this can only be solved by an explicit integer division operator or by a global integer division function as proposed in https://github.com/godotengine/godot-proposals/issues/11998#issuecomment-2745339504 .

daenvil avatar Nov 19 '25 09:11 daenvil

@daenvil Although I'd normally agree with you, I'd like to argue a counterpoint.

About the solution where you suppress the warning by recognizing patterns a / int(b), int(a) / b and int(a) / int(b) when both a and b were already type int even before the int(...) cast; I think it has a big advantage in which you don't need to introduce any new syntax or function. So I believe if anyone wants to make a PR, it would be more likely to be merged, as no syntax/API decision needs to be made.

More importantly, in my view, it's not mutually exclusive with other solutions. So IMO someone could do a PR for this without waiting for any sort of consensus, and we could still keep this ticket open if we also want something like a int div operator -- and I absolutely do want it (I think all languages should have them because using / for 2 different operations is absurd to me, just like how I hate addition operator + being used to concatenate strings instead of something like ><, but I digress). This way the main problem can be solved SOME way, and that doesn't stop it also being solved a BETTER way later (and by later I don't even mean Godot 5 necessarily).

The only downside I see is that I don't know if this sort of syntax pattern-based recognition for warning suppression is simple enough to implement. But if it is, I think it's a good starting point to not be stalled by any syntax/API decisions.

geekley avatar Nov 19 '25 13:11 geekley