Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PRVM: Return NaN/inf values for divisions by zero. #246

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

divVerent
Copy link
Contributor

Also improve accuracy of DIV_VF a bit.

This matches FTEQW.

We really should divide three times separately than compute the
reciprocal and multiply by that three times.

The latter allows two roundoff errors, the former only one.
…ot 0.

For now the division by zero warning is kept.

The old behavior is behind a cvar `prvm_gameplayfix_div0is0` to enable in
case this causes any trouble for anyone.

Fixed #240.
@hemebond
Copy link
Contributor

Compiles fine, and no crashes, but I don't see the new cvar, and doing float foobar = 1 / 0; does nothing; no crash or error or warning. Not sure how to test this.

@divVerent
Copy link
Contributor Author

divVerent commented Jan 27, 2025

Thanks; as for testing, you can't just write float foobar = 1 / 0, as this will be evaluated by the compiler.

To have a good chance of testing this, add to your game:

float autocvar_div0_a;
float autocvar_div0_b;
// In worldspawn:
void() worldspawn = {
  cvar_set("div0_a", "1");
  cvar_set("div0_b", "1");
  // The other stuff from before.
};
// Then in the StartFrame function:
void() StartFrame = {
  dprint("div0_a / div0_b = ", ftos(autocvar_div0_a / autocvar_div0_b), "\n");
  // The other stuff from before.
};

Then launch a game and perform on the console:

] developer 1
div0_a / div0_b = 1.000000
div0_a / div0_b = 1.000000
div0_a / div0_b = 1.000000
div0_a / div0_b = 1.000000
] set div0_b 0
WARNING: Attempted division ...
div0_a / div0_b = inf
WARNING: Attempted division ...
div0_a / div0_b = inf
WARNING: Attempted division ...
div0_a / div0_b = inf
] prvm_gameplayfix_div0is0 1
WARNING: Attempted division ...
div0_a / div0_b = 0.000000
WARNING: Attempted division ...
div0_a / div0_b = 0.000000
WARNING: Attempted division ...
div0_a / div0_b = 0.000000

I have not tested it yet, but this is the expected outcome.

@divVerent
Copy link
Contributor Author

Testing it myself now, now that I've written that code anyway. When using a more modern compiler, one can skip the cvar_set calls and just use:

var float autocvar_div0_a = 1;
var float autocvar_div0_b = 1;
void() StartFrame = {
  dprint("div0_a / div0_b = ", ftos(autocvar_div0_a / autocvar_div0_b), "\n");
  // The other stuff from before.
};

@divVerent
Copy link
Contributor Author

Yes, just confirmed it myself.

As a minor difference from expectations, DP prints -nan for the NaN result. I do not know why, but it does not really matter and is also pre-existing in ftos.

@divVerent
Copy link
Contributor Author

divVerent commented Jan 27, 2025

To be clear, the expectations are:

positive / 0 = inf
0 / 0 = nan or -nan
-0 / 0 = nan or -nan
negative / 0 = -inf
positive / -0 = -inf
0 / -0 = nan or -nan
-0 / -0 = nan or -nan
negative / -0 = inf

Generating a working -0 is left as an exercise to the reader.

Copy link
Contributor

@hemebond hemebond left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and working as documented in the PR comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants