How to generate an x87 positive NaN value from C89 code?

93 Views Asked by At

I'd like to generate the positive IEEE 754 32-bit float NaN value with integer representation 0x7fc00000 from C89 code.

Here is how I do it with GCC (not conforming to C89):

float mynan(void) { return __builtin_nanf(""); }

Here is how I do it in C89 code (assuming sizeof(int) == 4 or sizeof(long) == 4):

float mynan(void) {
  union { float f; unsigned u; unsigned long ul; } fu;
  if (sizeof(unsigned) == 4) {
    fu.u = 0x7fc00000U;
  } else {  /* Assume that sizeof(unsigned long) == 4. It's always >=4. */
    fu.ul = 0x7fc00000UL;
  }
  return fu.f;
}

Here is how I test it:

float mynan(void);
typedef char assert_int_size[sizeof(int) == 4 ? 1 : - 1];
int main(int argc, char **argv) {
  float f;
  union { float f; int i; } fi;
  (void)argc; (void)argv;
  fi.f = mynan();
  printf("0x%x\n", fi.i);  /* Prints 0x7fc00000 */
}

My question: is it possible to do it without union (or NAN in <math.h>, which is not C89) or memcpy (or memmove etc.), assuming x87 FPU and a nonoptimizing compiler (e.g. gcc -O0). I want the FPU to generate that NaN value for me as a result of some computation which I can trigger from C89 code.

I tried:

float mynan(void) {
  float f = 1L << 30;
  f *= f;  /* 2 ** 60. */
  f *= f;  /* 2 ** 120. */
  f *= f;  /* 2 ** 240, overflows 32-bit float, becomes INFINITY. */
  return f * 0.0f;  /* NaN. */
}

However, that prints 0xffc00000, which is the corresponding negative NaN value. How do I get the positive one?

I also tried:

float mynan(void) {
  float f = 1L << 30;
  f *= f;  /* 2 ** 60. */
  f *= f;  /* 2 ** 120. */
  f *= f;  /* 2 ** 240, overflows 32-bit float, becomes INFINITY. */
  return 0.0f - f * 0.0f;
}

This also returns the negative NaN.

I also tried:

float mynan(void) {
  float f = 1L << 30;
  f *= f;  /* 2 ** 60. */
  f *= f;  /* 2 ** 120. */
  f *= f;  /* 2 ** 240, overflows 32-bit float, becomes INFINITY. */
  return -(f * 0.0f);
}

This works with some compilers (e.g. gcc -O0), but not with others. One of the compilers generates code equivalent to return 0.0f - (f * 0.0f), which keeps the sign as negative.

1

There are 1 best solutions below

3
Mark Adler On

Try this:

#include <stdio.h>
#include <math.h>

int main(void) {
    float nan = fabs(sqrt(-1.));
    printf("%f\n", nan);
    printf("%08x\n", *(unsigned*)&nan);
    return 0;
}

Output from clang, gcc, and Visual C compile and run:

nan
7fc00000

Your mileage may vary.