If you’ve tried your computer’s patience, you may have encountered some valid yet odd results when doing math. For example, you may see NaN
output, which is computer-speak for “Not a number.” Or perhaps you’ve encountered INF
, infinity. The C library offers a way to test for these results before they’re output.
The fpclassify() function examines a floating point value or expression and returns an integer result based on the outcome. This function is declared in the math.h
header file and has this man page format:
int fpclassify(x);
Argument x
is a floating point expression. The integer value returned is represented by a defined constant that reports the result.
The fpclassify() function is a POSIX function, not part of the standard C library. It’s also a macro, so be careful how you implement it.
The following sample code demonstrates how to use the function, along with the defined constants available to interpret the result:
2025_02_08-Lesson-a.c
#include <stdio.h>
#include <math.h>
int main()
{
double a,b;
int r;
a = 1.0; b = 0.0;
/* divide by zero */
r = fpclassify(a/b);
switch(r)
{
case FP_NAN:
printf("%.1f/%.1f is not a number (NaN)\n",a,b);
break;
case FP_INFINITE:
printf("%.1f/%.1f is positive or negative infinity\n",a,b);
break;
case FP_ZERO:
printf("%.1f/%.1f is zero\n",a,b);
break;
case FP_SUBNORMAL:
printf("%.1f/%.1f is too small to be represented\n",a,b);
break;
case FP_NORMAL:
printf("%.1f/%.1f is a normal real number\n",a,b);
}
return 0;
}
In the code, I use double variables a
and b
to divide by zero. The result isn’t saved, but the fpclassify() function examines the expression, reporting its findings in variable r
. The switch-case structure examines the value returned and reports the results as compared with defined constants that represent the possibilities. Here’s a sample run:
1.0/0.0 is positive or negative infinity
Mathematicians may take issue with this result, as dividing by zero is generally accepted as undefined. Regardless, the fpclassify() function reported its results.
A sister function to ipclassify() is isfinite(). As you may guess, this function examines a floating point expression and reports whether the results are finite or infinite. Here’s sample code:
2025_02_08-Lesson-b.c
#include <stdio.h> #include <float.h> #include <math.h> int main() { printf("isfinite(NAN) = %s\n", isfinite(NAN) ? "TRUE" : "FALSE"); printf("isfinite(INFINITY) = %s\n", isfinite(INFINITY) ? "TRUE" : "FALSE"); printf("isfinite(0.0) = %s\n", isfinite(0.0) ? "TRUE" : "FALSE"); printf("isfinite(DBL_MIN/2.0) = %s\n", isfinite(DBL_MIN / 2.0) ? "TRUE" : "FALSE"); printf("isfinite(1.0) = %s\n" ,isfinite(1.0) ? "TRUE" : "FALSE"); printf("isfinite(exp(800)) = %s\n", isfinite(exp(800)) ? "TRUE" : "FALSE"); return 0; }
In the code, I’ve included the float.h
header file, which defines the DBL_MIN
constant. The isfinite() macro is used on various constants and expressions to determine whether the result is finite or infinite. Because I use the exp() function, this code must be built with the math library included; at the command prompt, add the -lm
switch. Here’s the output:
isfinite(NAN) = FALSE
isfinite(INFINITY) = FALSE
isfinite(0.0) = TRUE
isfinite(DBL_MIN/2.0) = TRUE
isfinite(1.0) = TRUE
isfinite(exp(800)) = FALSE
Values such as NaN and INF can be a surprise in a program’s output. If you want to avoid this condition, especially when you’re coding something severely important, such as launching a rocket into orbit or calculating a possibility for a first date. These functions can help spot conditions that may otherwise generate weird results.
Although it took them a while, since Visual Studio 2013 (
_MSC_VER >= 1800
) even Microsoftʼs Visual C++ Standard Library comes with a fpclassify() macro, so its existence can nowadays safely be assumed, I think.Thanks to FOSS itʼs also easy to take a look behind the curtains. Turns out, the two functions
__fpclassifyf (float f);
and__fpclassifyd (double d);
arenʼt too hard to implement… e.g. for doubleʼs:union IEEEd2bits;
freebsd-src/…/libc/include/fpmath.hunion IEEEd2bits {
double d;
struct {
unsigned int manl :32; // 52 bits for the significand (or mantissa)
unsigned int manh :20;
unsigned int exp :11; // 11 bits for the (biased) exponent
unsigned int sign :1; // sign bit: 0…positive, 1…negative
} bits;
};
int __fpclassifyd (double d);
freebsd-src/…/libc/gen/fpclassify.cIf it werenʼt for C99ʼs “strict aliasing”, fpclassifyd() could alternatively implemented as follows:
/* C99ʼs “strict aliasing rule” makes this dangerous…
`gcc` should be invoked with ‘-fno-strict-aliasing’ */
int fpclassifyd (double value)
{
long long const repr = *(long long *)&value;
{ long long significand = repr & ((1LL << 52) – 1);
unsigned int biased_exponent = (repr >> 52) & ((1L << 11) – 1);
if (biased_exponent == 0)
{
if (significand == 0)
return (FP_ZERO);
return (FP_SUBNORMAL);
}
else
if (biased_exponent == 2047)
{
if (significand == 0)
return (FP_INFINITE);
return (FP_NAN);
}
}
return (FP_NORMAL);
}
Also, as can be seen in s_isfinite.c, isfinite() is really just a simple return (biased_exponent != 255); for floatʼs / return (biased_exponent != 2047); for doubleʼs.
Cool stuff. I enjoy how you look under the hood to see how this stuff really works. Thank you!
I had hoped this would be interesting, glad you like it!
Sadly, the respective standard is behind a paywall. Nonetheless I think that the “IEEE Std 754-1985” standards document⁽¹⁾ is something that should at least be read once by everybody interested in programming.
⁽¹⁾ Later editions of the standard are only really of interest if one wants to know how the encoding for BID (binary integer decimal) floating-point numbers is specified (for which C23 finally added support through the newly added
_Decimal32
,_Decimal64
, and_Decimal128
data types).