Godot-proposals: Division by zero should return +INF

Created on 14 Aug 2020  Â·  25Comments  Â·  Source: godotengine/godot-proposals

Describe the project you are working on:
I've been building a trading system in Godot, and my code has many mathematical transformations.

Describe the problem or limitation you are having in your project:
I'm getting a bit tired of crashes whenever I accidentally don't check if I divide by zero or not.

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
I notice that in JS 1/0 is Infinity, and I'd suggest this is a good solution.

In python 1/0 is a runtime error, but since most stats systems use numpy or pandas anyway, they usually take care of this to be +INF.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:

# Calculate % change from one candle to the next
y_change = (next_candle.c - curr_candle.c) / curr_candle.c

and the following will commonly fail, with an error (Division by zero):

curr_candle.c / y_change

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

y_change is quite commonly zero, and so I have to always add an extra check:

```gdscript
y_change = (next_candle.c - curr_candle.c) / curr_candle.c
if y_change == 0:
y_change = 0.000000001
````

Is there a reason why this should be core and not an add-on in the asset library?:
This is a bit frustrating and leads to many errors when I accidentally forget to check this.

I'd be interested in hearing other contributors thoughts, as this lies outside the realm of normal game dev stuff, and perhaps returning +INF when we divide by zero is not a great solution for some?

Additional
Submitted as bug report, redirected to proposal

core

Most helpful comment

1 / 0 is supposed to be an error. The input types are int and int, so the output should be int. Infinity only exists in the context of floats; there is no way to represent infinity with integers.

1.0 / 0.0 is supposed to be infinity, but I just tested in 3.2 and it seems to be an error instead.

All 25 comments

Thanks for reopening it here as a proposal. Can you edit the proposal to follow the template?

@Calinou I've updated to match the template, thanks for the reminder!

1 / 0 is supposed to be an error. The input types are int and int, so the output should be int. Infinity only exists in the context of floats; there is no way to represent infinity with integers.

1.0 / 0.0 is supposed to be infinity, but I just tested in 3.2 and it seems to be an error instead.

Screenshot 2020-08-14 at 22 15 34

Screenshot 2020-08-14 at 22 16 11

Screenshot 2020-08-14 at 22 16 30

Noted, thanks for your input @aaronfranke! It does make sense that integer division should not produce infinity, as it's an error case. Only for real numbers should 1/0 approach infinity.

@Calinou perhaps you know better if this should be then marked again as a bug? I can reopen the other thread if you think it's more appropriate?

@tavurth I think this is still feature proposal territory, as getting an error may be desired in many cases. I'd expect the physics engine to go awry if you feed it infinite coordinates for velocity, for instance.

At the same time, we'd not be taking 0 as +INF, as 0 is 0.

Instead we'd be taking division by 0 as +INF, which is mathematically correct.

Let's take the example of 1 / velocity (v):

v = 1; 1 / v = 1
v = 0.1; 1 / v = 10
v = 0.01; 1 / v = 100
v = 0.0001; 1 / v = 10000
v = 0.000001; 1 / v = 1000000
v = 0.00000001; 1 / v = 100000000
v = 0.0000000001; 1 / v = 10000000000
v = 0.000000000001; 1 / v = 1000000000000

As we can see, division by (close to) zero rapidly approaches infinity. Therefore it seems closer to a bug from my perspective.

I don't know what happens in C# (unity) or other game focused languages when a division by zero occurs. Perhaps others can weigh in on this?

Doesn't INF break the code anyways? E.g. you move something and accidentally divide velocity by 0, so the object moves to infinity. Or you divide damage by 0 and insta-kill everything. Or in the code you provided you suddenly get INF% change, which might break other calculations (e.g. when you try to subtract from infinity etc.). In what case it wouldn't be unexpected (i.e. INF checks wouldn't be needed)?

Screenshot 2020-08-14 at 23 07 11

@KoBeWi division by +INF gives zero and causes no errors.

I'm using the Godot built in +INF and -INF quite a bit in my projects, and I'm glad that they've been included. I feel like this error is a bit of an oversight, as when we graph an approach to division by floating zero, it _approaches_ infinity.

From my perspective (1 / 0.0) == +INF and (-1 / INF) == -INF

Dividing +INF by any number should return +INF which is expected, as countable infinity should be non-divisible.

Screenshot 2020-08-14 at 23 39 33

If you want to use dividing by float 0, you can compile Godot with removed lines from 217 to 220 in
https://github.com/godotengine/godot/blob/aded76cb84fd1081279d2ed11fd73b553c6e6b4e/core/variant_op.cpp#L216-L222

I don't think that is good idea to allow dividing by float 0 to produce INF, because in most cases this isn't intentionally.

@qarmin thank you for linking the relevant code!

I feel like asking scientists to comment out a few lines and recompile the engine is a bit much if we want to allow adoption by other types of consumer than game developers?

From my perspective 1 / 0 should never result in a weird case for game development, but perhaps adding it as a warning in the same area as we currently have for other such items (project dev settings) would be a more friendly approach?

Changing it to warning instead of error isn't a bad idea actually.

1 / 0 should be an error, but 1.0 / 0.0 could give a warning maybe.

@tavurth In the last screenshot you posted, you print 1 / INF but the math is INF / 1.0.

Also, in the example you posted, you have v = 0.000000000001; 1 / v = 1000000000000, but you should also consider that v = -0.000000000001; 1 / v = -1000000000000, which is another reason why float operands are required (floats have signed zero, so we can have 1.0 / 0.0 giving infinity and 1.0 / -0.0 giving negative infinity).

Yep, you're correct about screenshot will fix...

I agree about the operands, 1 / -0.0 == -INF as well as -1 / 0.0 == -INF

Screenshot 2020-08-14 at 23 44 57

Screenshot 2020-08-14 at 23 45 46

What value does having an INF number provide?

Uses for INF

There are many uses for INF in programming, and many more in data science and statistics.
A simple example of an everyday use is to find the min-max of a range of values:

var max_val = -INF
var min_val = +INF

for value in values:
    max_val = max(max_val, value)
    min_val = min(min_val, value)

We can be sure that min_val and max_val will always show us the true extents, and is cross platform.
Infinity is useful for many other areas of coding, but I won't go into them here.

This proposal

This proposal is primarily about reducing an error case when we divide by zero, which should in my opinion give infinity, but currently throws an error.

It's annoying as it leads to many needless errors while developing, and every time I end up with a value which could be zero, I have to check it and set to 0.00000000001 if it is so. (prevent this error).

I just tested, and running print(1 / 0.0) returns INF while the game is in production mode (exported).

There's no way this could break any games that are not already broken, as Godot already works in the expected way for production exports.

This is an editor only (debug only) change, and would result in one less error while developing stats projects in Godot.

1.0 / 0.0 is NOT suposed to be infinity. If you add an infinite number of zeros together you dont get one.

Say you take y = 1/x and graph it. On one side it converges to INF and on the other side to negative INF. So which one is it? Well its neither because 1 can not be divided by 0. The IEEE 754 for floating point math standard specifies that there should be an Undefined Number exception raised (or just crash).

There IS a Complex Infinite that can be returned for zero division maths in the complex number realm, but for your standard float or integer math, returning INF is incorrect and should be changed.

So yes, changing this to INF is a terrible idea because we want maths to behave in a deterministic and natural way, and this aint it.

Hi @shayneoneill,

If you take a quick look at this notebook you'll see that common stats libraries treat 1.0 / 0.0 as +INF and -1.0 / 0.0 as -INF.

Screenshot 2020-08-15 at 13 32 38

Godot treats these numbers the same way in production. (Silently returns +INF or -INF)

This proposal is about removing the debug mode crash when we have a float division by zero, as it's inconsistent with the production code usage.

The reason for this is that for stats usage, division by zero should give a very large (infinitely large) number, not a crash.

At the moment I have to add a check every time I'm about to divide by anything, otherwise my debug mode build will break.

It's a shame to have this happen in debug, as in production I can simply omit this check.
I imagine so many conditionals have at least some performance impact on my production code, and they certainly impact my code readability.

if my_maybe_zero_var == 0:
    my_maybe_zero_var = 0.000000000001

No, a division by zero is simply undefined. Changing math rules for the sake of avoiding a simple if check would be the worst idea ever, being both false and confusing.

In some libraries the division by zero may lead to +INF, but usually this is because for big dataframes, you usually prefer not having a single row making the whole processing fail (other libs may fill the value with NaN). If you try dividing by zero in core python, you will get an error.

Hi @groud!

This proposal is about the removal of a crash when in debug mode.

As the code here shows (@hpvb), this proposal is already in effect in production mode exports. This bug only appears when debug mode is enabled.

After @shayneoneill made reference to the IEEE Std 754, and now your assumption that we would be changing math by taking this proposal, I would like to set the record straight..

The IEEE-754 standard is quite clear on page 37.

The default result of divideByZero shall be an ∞ correctly signed according to the operation

So the global standard is that division by zero, should return ±∞ or ±INF.

Please note that Godot currently already does this

Relevant passage

Screenshot 2020-08-15 at 15 20 04

More information

Stack Overflow

Yes, it make sense to avoid crashes in production at any cost. But in debug mode, the program should crash when you do such operation. A division by zero is always something the user should handle themselves, and do not rely on arithmetic with INF. It's a lot better to have your program crash so that you can fix the problem, instead of having weird behaviors you don't understand why.

Given the IEEE above, I would think that INF is a better choice than outright crashing, BUT it would need to be well-documented so that people do not

rely on arithmetic with INF

Also, this likely would break compat?

I certainly see the reason behind wanting to avoid accidental /0, yet forcing stats users to restart the app every time they forget a check is overbearing, not to mention the performance overhead which persists after development has ended.

Perhaps we can simply rely on INF result, but give a toggleable warning in the dev console, in the same fashion as unused_signal or other warning?

Thoughts on moving to warning rather than crash while in dev mode?

but forcing stats users to restart the app every time they forget a check is overbearing

You don't need to restart, there's a resume button next to Stop.

You don't need to restart, there's a resume button next to Stop.

Unfortunately due to https://github.com/godotengine/godot/issues/24684 changing and continuing after a breakpoint doesn't work for most cases where the user has used yield in the project. (At least it breaks for me in most cases)

In this case /0 pretty much always acts as a crash. In the no yield case, it acts as an inescapable breakpoint.

From my perspective there should some be way to disable a breakpoint without having to pause every iteration.

Was this page helpful?
0 / 5 - 0 ratings