How Whacky is that Real Number?

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.

3 thoughts on “How Whacky is that Real Number?

  1. 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.h
    union 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.c

    If 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.

  2. Cool stuff. I enjoy how you look under the hood to see how this stuff really works. Thank you!

  3. 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).

Leave a Reply