Allow floating-point division by zero in ZScript

Post a reply

Smilies
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :geek: :ugeek: :!: :?: :idea: :arrow: :| :mrgreen: :3: :wub: >:( :blergh:
View more smilies

BBCode is OFF
Smilies are ON

Topic review
   

Expand view Topic review: Allow floating-point division by zero in ZScript

Re: Allow floating-point division by zero in ZScript

by argv » Sat Jul 28, 2018 5:19 pm

AFADoomer wrote:For brevity's sake, I usually end up putting in a "max([divisor that might end up as zero], [arbitrary small number]);" call in cases like this...

e.g.:

Code: Select all

TweenChange = 1. / (max(time, 0.0001) * TICRATE);
Not sure if that slows things down internally any more/less than using if statements to the same effect, but I haven't noticed any issues so far.
Good idea. As Graf confirmed, that's faster than if branches.

I've replaced the if with similar code:

Code: Select all

TweenChange = 1. / (max(time, double.epsilon) * TICRATE);
Thanks for the idea. :)

Re: Allow floating-point division by zero in ZScript

by AFADoomer » Sat Jul 28, 2018 5:04 pm

You are correct. I started with a different example, and ended up putting the max in the wrong place when I changed it up...

Re: Allow floating-point division by zero in ZScript

by Gez » Sat Jul 28, 2018 3:58 pm

AFADoomer wrote:For brevity's sake, I usually end up putting in a "max([calculations], [arbitrary small number]);" call in cases like this...

e.g.:

Code: Select all

TweenChange = max(1. / (time * TICRATE), 1);
Not sure if that slows things down internally any more/less than using if statements to the same effect, but I haven't noticed any issues so far.
I don't get it -- this doesn't avoid the potential division by zero, does it?

I'd expect more something like this:

Code: Select all

TweenChange = 1. / (max(time, 0.0001) * TICRATE);

Re: Allow floating-point division by zero in ZScript

by Graf Zahl » Sat Jul 28, 2018 3:10 pm

No. It's a min followed by a max.

Re: Allow floating-point division by zero in ZScript

by argv » Sat Jul 28, 2018 3:09 pm

Is clamp also a single VM operation?

Re: Allow floating-point division by zero in ZScript

by Graf Zahl » Sat Jul 28, 2018 10:40 am

In terms of scripting, min and max are VM opcodes so they are very efficient. An 'if' requires a branch which is 2 more costly instructions most of the time.

Re: Allow floating-point division by zero in ZScript

by AFADoomer » Sat Jul 28, 2018 10:37 am

For brevity's sake, I usually end up putting in a "max([divisor that might end up as zero], [arbitrary small number]);" call in cases like this...

e.g.:

Code: Select all

TweenChange = 1. / (max(time, 0.0001) * TICRATE);
Not sure if that slows things down internally any more/less than using if statements to the same effect, but I haven't noticed any issues so far.

EDIT: Fixed incorrect example, in case someone else stumbles across this post. Thanks, Gez!

Re: Allow floating-point division by zero in ZScript

by Gutawer » Sat Jul 28, 2018 9:51 am

argv wrote:And it makes sense to me: as the divisor approaches zero, the result approaches infinity, so it follows that a divisor of zero ought to result in infinity.
The result only approaches infinity as you take the limit in the positive numbers, taking the limit in the negative numbers gives a limit of -infinity, so this has to be undefined because it has (at least) two limits. As Gez points out, sometimes you can have more than two, like with 0/0 = x which, using algebra, can be turned into 0 = 0x, which is true for every number, so therefore x must be undefined here too but with an infinite number of results.

Re: Allow floating-point division by zero in ZScript

by Gez » Sat Jul 28, 2018 7:41 am

argv wrote:And it makes sense to me: as the divisor approaches zero, the result approaches infinity, so it follows that a divisor of zero ought to result in infinity.
Nothing makes sense when division by zero is involved.

Keep in mind that division is the inverse operation of multiplication. That is to say, if you have X × Y = Z, then you also have Z ÷ Y = X. Based on that, if some random number, let's say 12, is divided by zero and gives you infinity, then that means that infinity multiplied by zero gives you 12. Do you see the problem?

Even better: X ÷ X = 1. This is true for every number. So what is X = 0? Shouldn't you get 0 ÷ 0 = 1? But wait, 0 ÷ X = 0; this also is true for any number. So 0 ÷ 0 = 0? But it's also equal to 1 and to infinity!

Therefore, if X ÷ 0 is legal, then 0 = 1 = infinity. Good luck getting the rest of your maths to still work after that.

Re: Allow floating-point division by zero in ZScript

by dpJudas » Sat Jul 28, 2018 4:55 am

argv wrote:IEEE 754 disagrees with you on this point. And it makes sense to me: as the divisor approaches zero, the result approaches infinity, so it follows that a divisor of zero ought to result in infinity.
Alright, I only tested the 0.0/0.0 case before writing that. Nevertheless, you can't apply that kind of logic you're doing here mathematically. If you read the link closely this is about trying to handle underflow situations in a more graceful way.

Remember that infinity in floating point doesn't mean mathematical infinity. It means the value overflowed for what can be represented with this number of bits.

Re: Allow floating-point division by zero in ZScript

by argv » Sat Jul 28, 2018 1:06 am

dpJudas wrote:Division by zero should never result in infinity. The only correct value is NaN.
IEEE 754 disagrees with you on this point. And it makes sense to me: as the divisor approaches zero, the result approaches infinity, so it follows that a divisor of zero ought to result in infinity.
Graf Zahl wrote:Considering how much trouble NaNs have been with the JSON serializer, let's better not think about this any further.
Ok, fair enough.

Re: Allow floating-point division by zero in ZScript

by Graf Zahl » Sat Jul 28, 2018 12:32 am

Considering how much trouble NaNs have been with the JSON serializer, let's better not think about this any further.

Re: Allow floating-point division by zero in ZScript

by dpJudas » Fri Jul 27, 2018 10:33 pm

Division by zero should never result in infinity. The only correct value is NaN.

There are pros and cons for allowing division by zero. The pro is obviously that it doesn't crash. On the other hand, you're now allowing NaN values to get stored and the rest of the engine has to be able to handle that. It also gets significantly harder to figure out what went wrong when you suddenly see NaN values a million miles away from the code that caused it.

Why not just check for zero the places where that is an allowed value? If travelTime gets a NaN value you have to check for that anyway.

Re: Allow floating-point division by zero in ZScript

by argv » Fri Jul 27, 2018 10:15 pm

The other time I ran into this problem, I needed to compute how long it would take an actor to reach a destination at a given speed (ignoring portals and such), like so:

Code: Select all

Actor a = …;
Vector3 destination = …;
let travelTime = (destination - a.Pos).Length() / a.Speed;
Again, division by zero resulting in infinity would be correct here: an object with zero velocity takes infinite time to reach its destination.

I didn't end up using that code, but it did strike me as interesting at the time. It was the first time I remember writing a potential division by zero on purpose—or I would have, if the engine allowed it.

Of course, this would still yield incorrect results if the distance to the destination is also zero. 0/0 usually results in NaN or infinity, but in this case the actor would reach its destination in zero time despite zero speed, since it's already there.

Re: Allow floating-point division by zero in ZScript

by argv » Fri Jul 27, 2018 10:05 pm

From Use To Pickup:

Code: Select all

		if (HighlightState == FADING_IN || HighlightState == FADING_OUT) {
			double time;
			if (HighlightState == FADING_IN)
				time = ps.Settings.FadeInTime.GetFloat();
			else
				time = ps.Settings.FadeOutTime.GetFloat();
			
			// Avoid division by zero.
			// Division by zero would actually yield correct results (positive infinity), but the engine crashes instead of passing such a value through, so we have to special-case it.
			if (time <= 0.)
				TweenChange = 1;
			else
				TweenChange = 1. / (time * TICRATE);
			
			if (HighlightState == FADING_OUT)
				TweenChange = -TweenChange;
		}
		
		if (TweenChange)
			Tween = clamp(Tween + TweenChange, 0., 1.);
It needs to compute how fast an animation is to run, in floating-point change per tic (TweenChange). For user-friendliness, however, the menu options represent the animation speed as total duration in seconds (ps.Settings.Fade{In,Out}Time). To get from the latter to the former, I compute 1/(time*TICRATE).

It's perfectly valid to set the durations to 0, to turn off the animation entirely. This would be represented by TweenChange ≥ 1. ∞ ≥ 1, so an infinite result would result in correct behavior (clamp(Tween + ∞, 0, 1) = 1), and wouldn't require the special case.

Top