Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is `isnan()` as fast as testing equality?

In C, is testing if a float is NaN as fast as testing if two floats are equal? That is is isnan() as fast a simple equality test between two floats?

My particular interest is using gcc on a standard modern Intel/AMD platform.

Here is a sample piece of C code.

#include <math.h>
int main(double x)
{
  return isnan(x);
}
like image 555
graffe Avatar asked Oct 15 '25 23:10

graffe


2 Answers

Using GCC on x64, math.h's isnan(float) compiles to

jmp __isnanf

Using tail-call optimization, but effectively calling a function. The called function will have to do something equivalent to the code a bit down, at least I don't see any faster way to implement it. That leaves the question how it compares to a comparison unanswered however.

But that doesn't say anything about how fast "testing if a float is NaN" is, because there isn't just the one way to do it. The most direct way,

int isnan2(float x)
{
  return x != x;
}

Is literally the same thing as comparing floats at the C level. But GCC makes this:

xor eax, eax
ucomiss xmm0, xmm0
setp    al
ret

Which is not quite the same thing as comparing two floats, but close, and in fact a bit faster. Testing for equality means the unordered case tested, as it is here, but then the z flag also has to be tested, like this (from gcc again)

xor eax, eax
mov edx, 1
ucomiss xmm0, xmm1
setp    al
cmovne  eax, edx
ret

Bonus: using <cmath> makes isnan compile to the same thing as comparing a float to itself, see the linked question for why.

Godbolt link for convenience

I now see you actually had double, but that doesn't change anything qualitatively.

like image 124
harold Avatar answered Oct 17 '25 14:10

harold


For GCC (and perhaps Clang as well), the catch of optimizing isnan() function call is whether signaling NaNs are present in the code, and whether floating point exceptions would be raised on encountering a signaling NaN.

GCC assumes -fno-signaling-nans by default and so isnan(x) can be safely optimized into (x != x), which, on x86 with SSE2, would translate into a UCOMISS or UCOMISD instruction.

Setting -fsignaling-nans disables such optimization.

ISO/IEC TS 18661-1 (which is incorporated into the upcoming C23 standard) clarified the behavior of isnan() that the function never throws any exception, even when the argument is a signaling NaN. (This make it irony that the "signaling" NaN in IEEE 754 doesn't always signal.)

The (x != x) expression, on the other hand, does throw FE_INVALID exception (aka. "Invalid Operation") when x is a signaling NaN.

Here is a quick comparison table for behaviors of various expressions with "quiet" NaNs and "signaling" NaNs:

              | isnan(x) | (x != x)  | (x >= x)   | isgreaterequal(x,x)
--------------+----------+-----------+------------+--------------------
-Wfloat-equal | no warn  | warn      | no warn    | no warn
--------------+----------+-----------+------------+--------------------
Finite number | false    | false     | true       | true
Infinity      | false    | false     | true       | true
NaN (quiet)   | true     | true      | FPE; false | false
SNaN          | true     | FPE; true | FPE; false | FPE; false

Notes:

  • isnan() and isgreaterequal() technically return values of int type, not bool. The "true" and "false" in the table correspond to nonzero and 0 values, respectively.
  • "FPE" in the table indicates a floating point exception would be thrown; specifically the "Invalid Operation" exception.

If your application code does not use signaling NaNs at all (or you use them only for debugging), then you might define a macro for a "cheaper" isnan which won't emit calls to the libm isnan() function:

#include <math.h>
#ifdef isgreaterequal
#define isnan_cheap(x) (!isgreaterequal(x, x))
#endif

As isgreaterequal macro is type generic in C, this custom macro would be type generic as well.

like image 44
Explorer09 Avatar answered Oct 17 '25 15:10

Explorer09



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!