Why does isnan(x) exist if x != x gives the same result?

if the same result can be obtained using natural capabilities of the language?

C does not specify x == x iff x is not NaN. Many implementations do that though. C does not require adherence to IEEE_754. isnan(x) is well defined.

Use isnan(x) for portable code.


C in Representations of types (since C99) has

Two values (other than NaNs) with the same object representation compare equal, but values that compare equal may have different object representations.

... but that does not specify the behavior of comparing 2 NANs.


When __STDC_IEC_559__ (akin to adherence to IEEE-754) is defined as 1 (something not required by C), then

"The expression x != x is true if x is a NaN."

"The expression x == x is false if x is a NaN."

When __STDC_IEC_559__ is not defined as 1, exercise caution about assuming behavior in the edges of floating point math such as NAN equality.


[Edit to address some comments]

C, on the corners of FP math, lack the specifics of IEEE-754. C89 allowed NANs as evidenced by references to IEEE-754, yet lacked isnan(x). There was no "Two values (other than NaNs) with the same object representation compare equal, ..." to guide either. At that time, x==x for NAN was not specified. With C99, rather than break or invalidate prior code, isnan(x) is defined as a clear NAN test. As I see it, x==x remains unspecified for NANs, yet it is commonly results in false. isnan(x) also provides code clarity. Much about C and NAN is fuzzy: round tripping payload sequences, signaling encoding/discernment, NAN availability, ...


Are there any functional differences between isnan(x) and x != x?

In addition to the well defined functionality of isnan(x) versus x != x discussed above, some obscure ones:

  • isnan(x) evaluates x once versus twice for x != x. Makes a difference if x was some expression like y++.

  • isnan(x) operates on the sematic type. This makes a difference when "the implementation supports NaNs in the evaluation type but not in the semantic type.". x != x operates on the evaluation type. Research FLT_EVAL_METHOD for more detail.


Why isnan(x) exists if x != x (or x == x) gives the same result?

Because they don't always give the same result.

For example, GCC when compiling with -funsafe-math-optimizations replaces

x - x

with

0.0

so ( x == x ) can be true even if x is NaN.

-funsafe-math-optimizations is also enabled if either -fast-math or -Ofast is specified:

...

-ffast-math

Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast.

This option causes the preprocessor macro __FAST_MATH__ to be defined.

This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.

...

-funsafe-math-optimizations

Allow optimizations for floating-point arithmetic that (a) assume that arguments and results are valid and (b) may violate IEEE or ANSI standards. When used at link time, it may include libraries or startup files that change the default FPU control word or other similar optimizations.

This option is not turned on by any -O option since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications. Enables -fno-signed-zeros, -fno-trapping-math, -fassociative-math and -freciprocal-math.

...

So there are cases where you might want to use floating point optimizations for performance reasons but still need to check for NaN, and the only way to do that is to explicitly check with something like isnan().

Also, the C standard states in 6.2.6.1p4:

Two values (other than NaNs) with the same object representation compare equal

The functionality necessary to implement that requires the ability to check for NaN in some way separate from comparing the object representation (bits). So isnan() functionality is a prerequisite for implementing "x == x is false if x is NaN".

There has to be some type of functionality to check for NaN independent of x == x else it's just an infinitely recursive definition that can't be implemented.

If all you need to do is check for NaN and don't need to waste CPU cycles actually doing the comparison, exposing that functionality in something like isnan() can be a performance benefit.

Tags:

C

Ieee 754

Nan