I was skeptical about this article's claim about LLMs so I tried it out myself. Looks like there are several layers of pedantry involved, for example, strictly speaking in base C it's undefined behavior to divide by 0.0, even for floats, so the author's initial implementation is incorrect and a optimizer could elide the call to isfinite in certain scenarios where the denominator is 0.0.
Presumably the airtight implementation assuming Annex F is as follows:
#pragma STDC FENV_ACCESS ON
int my_div(float x, float y, float* r) {
if(!r) {
return -1;
}
fenv_t environment;
if(feholdexcept(&environment) != 0) {
return -1;
}
float result = x / y;
int failed = fetestexcept(
FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
if(fesetenv(&environment) != 0 || failed != 0) {
return -1;
}
*r = result;
return 0;
}
When I asked about the author's implementation I got the response that the author's implementation is deficient in several areas: - Underflow produces a finite result and passes this check.
- Overflow can produce a finite maximum value under some rounding modes.
- Quiet NaNs and valid infinite results fail this check, even without an arithmetic exception.
It also assumes floating-point traps are disabled. Annex F defines the arithmetic behavior; your function still needs a chosen definition of “failure.”
So the answer really is... safely dividing two floats really depends on what trade-offs you're willing to make, and the original reply of just checking for a 0 denominator is honestly the most sensible, portable, and safest.