10

For the robustness reason, I want check if a float number is IEEE-754 +-Inf or IEEE-754 Nan. My code is in the following, I want know if it is correct:

 #define PLUS_INFINITE          (1.0f/0.0f)
 #define MINUS_INFINITE         (-1.0f/0.0f)
 #define NAN                    (0.0f/0.0f)

 float Local_Var;
 /*F is a float numnber.*/
 if((unsigned long)(F) == 0x7f800000ul)
   {
    Local_Var = PLUS_INFINITE;
   }
 elseif((unsigned long)(F) == 0xff800000ul)
   {
    Local_Var = MINUS_INFINITE;
   }
   /*fraction = anything except all 0 bits (since all 0 bits represents infinity).*/
 elseif((((unsigned long)(F) & 0x007ffffful) != 0ul )
         &&((unsigned long)(F) == 0x7f800000ul))
        || 
        (((unsigned long)(F) & 0x807ffffful) != 0ul )
        &&
        ((unsigned long)(F) == 0xff800000ul))
   {
    Local_Var = NAN; 
   }
   else{}
Stack Over
  • 425
  • 2
  • 8
  • 18
  • 10
    As of C99, `` has the functions (or macros) `isnan(x)`, `isfinite(x)`, `isinf(x)` and `isnormal(x)`. – M Oehm Mar 22 '16 at 09:28
  • I develop a code for embedded systems, and using the standard library like math.h is not permitted. – Stack Over Mar 22 '16 at 09:33
  • 2
    I don't understand the downvote on this question. Seems well-researched and well-posed to me. – Bathsheba Mar 22 '16 at 09:44
  • 1
    a NaN is not equal to anything, not even itself. so one way is `f!=f` – sp2danny Mar 22 '16 at 10:23
  • 4
    1) "using the standard library like math.h is not permitted" critical restrictions like this belong in the post. 2) Disagree with the "code for embedded systems" --> "math.h is not permitted" rational as it is only the functions in that incur an issue. Constants & marcos are not a problem. – chux - Reinstate Monica Mar 22 '16 at 14:08
  • Is the `isfinite(x)` in your `` a macro that you can simply copy? – chux - Reinstate Monica Mar 22 '16 at 15:18

3 Answers3

18

C99 has macros for the classification of floating-point numbers:

fpclassify(x) returns one of:

  • FP_NAN: x is not a number;
  • FP_INFINITE: x is plus or minus infinite;
  • FP_ZERO: x is zero;
  • FP_SUBNORMAL: x is too small to be represented in normalized format or
  • FP_NORMAL: normal floating-point number, i.e. none of the above.

There are also shortcuts that check for one of these classes, which return non-zero if x is what :

   isfinite(x)
   isnormal(x)
   isnan(x)
   isinf(x)

The argument x can be any floating-point expression; the macros detect the type of the argument and work for float and double.

EDIT: Since you don't want to use (or cannot use) <math.h>, you could use other properties of nan and inf to classify your numers:

  • nan compares false to all numbers, including to itself;
  • inf is greater than FLT_MAX;
  • -inf is smaller than -FLT_MAX.

So:

#include <stdlib.h>
#include <stdio.h>
#include <float.h>

int main()
{
    float f[] = {
        0.0, 1.0, FLT_MAX, 0.0 / 0.0, 1.0/0.0, -1.0/0.0
    };
    int i;

    for (i = 0; i < 6; i++) {
        float x = f[i];

        int is_nan = (x != x);
        int is_inf = (x < -FLT_MAX || x > FLT_MAX);

        printf("%20g%4d%4d\n", x, is_nan, is_inf);
    }

    return 0;
}

In this solution, you must adapt the limits if you want to use double.

M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • Is "`nan` compares false to all numbers, including to itself;" a result of the IEEE-754 spec, C spec or both? – chux - Reinstate Monica Mar 22 '16 at 13:57
  • The C standard doesn't require that a floating-point implementation have nans. I think `nan != nan` is a property of IEEE-754 floats, but that's not authoritative. – M Oehm Mar 22 '16 at 14:42
  • Although `x != x` is a _good_ and the right idea, embedded systems are known for using "parts" of IEEE-754 - lacking full compliance. AFAIK, OP is using `IEEE-754` _formatted_ data, but code's implementation of `IEEE-754` _functionality_ may be weak. – chux - Reinstate Monica Mar 22 '16 at 15:18
  • @chux: I'm not familiar with development on embedded systems. The original question doesn't mention any particular restrictions on IEEE-754. – M Oehm Mar 22 '16 at 15:22
  • [Is it safe to assume floating point is represented using IEEE754 floats in C?](http://stackoverflow.com/a/31973105/2410359) is a good ref. The core issue is OP says "robustness", "IEEE-754", "embedded", "math.h is not permitted" and posts incorrect and non-portable code. There is so much in conflict amongst these 5, that I doubt even a good answer like yours will suffice. – chux - Reinstate Monica Mar 22 '16 at 15:31
2

Casting floats to longs like that is wrong. It should be either a union, or a type-punned pointer.

Here's a working example from dietlibc (with doubles):
https://github.com/ensc/dietlibc/blob/master/lib/__isinf.c https://github.com/ensc/dietlibc/blob/master/lib/__isnan.c

Musl has a shorter fpclassify, and also proper constants for floats:
http://git.musl-libc.org/cgit/musl/tree/src/math/__fpclassifyf.c

arsv
  • 1,176
  • 9
  • 11
  • Downvoted: Pointer type punning breaks strict aliasing rules and is UB. – user694733 Mar 22 '16 at 10:22
  • 2
    @user694733: yes, but type punning is what the OP obviously had in mind. This answer says why it didn't work. – M Oehm Mar 22 '16 at 10:40
  • @MOehm Type punning with union is fine, assuming that one is using compiler that supports C standard which is new enough. Suggesting to use pointers to do type punning is *never* correct. – user694733 Mar 22 '16 at 12:23
  • @user694733 Concerning "type punning is _never_ correct" What is wrong with type punning `int i = foo(); unsigned u = *((unsigned *) &i);`? – chux - Reinstate Monica Mar 22 '16 at 14:04
  • @chux Problem is the lack of guarantees. Compiler optimizer could legally break your code. And there is always work-around, in this case: `unsigned u = i;`. – user694733 Mar 22 '16 at 15:16
0

Best to use the fpclassify() functions of @M Oehm answer

Alternatives:

float F;
if (F <= FLT_MAX) {
  if (F >= -FLT_MAX) {
    puts("Finite");
  } else {
    puts("-Infinity");
  }
} else {
  if (F > 0) {
    puts("+Infinity");
  } else {
    puts("NaN");
  }
}

If code wants to mess with the bits and assuming float are in binary32 format:

assert(sizeof (float) == sizeof (uint32_t));
union {
  float f;
  uint32_t u32;
} x;
x.f = F;

Masks depend on relative endian of float and uint32_t endian. They usually are the same.

// Is F one of the 3 special: +inf, -inf, NaN?
if (x.u32 & 0x7F800000 == 0x7F800000) {
  if (x.u32 & 0x007FFFFF) {
    puts("NaN");
  } else if (x.u32 & 0x80000000) {
    puts("-Inf");
  } else {
    puts("+Inf");
  }
}
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256