如何在 C 语言中使用 nan 和 inf?

我有一个数值方法,如果出现错误,它可以返回 nan 或 inf,为了测试目的,我想暂时强制它返回 nan 或 inf,以确保正确处理这种情况。有没有一种可靠的 独立于编译器的方法来在 C 中创建 nan 和 inf 的值?

在谷歌了大约10分钟之后,我只能找到与编译器相关的解决方案。

175806 次浏览

There is no compiler independent way of doing this, as neither the C (nor the C++) standards say that the floating point math types must support NAN or INF.

Edit: I just checked the wording of the C++ standard, and it says that these functions (members of the templated class numeric_limits):

quiet_NaN()
signalling_NaN()

wiill return NAN representations "if available". It doesn't expand on what "if available" means, but presumably something like "if the implementation's FP rep supports them". Similarly, there is a function:

infinity()

which returns a positive INF rep "if available".

These are both defined in the <limits> header - I would guess that the C standard has something similar (probably also "if available") but I don't have a copy of the current C99 standard.

A compiler independent way, but not processor independent way to get these:

int inf = 0x7F800000;
return *(float*)&inf;


int nan = 0x7F800001;
return *(float*)&nan;

This should work on any processor which uses the IEEE 754 floating point format (which x86 does).

UPDATE: Tested and updated.

I'm also surprised these aren't compile time constants. But I suppose you could create these values easily enough by simply executing an instruction that returns such an invalid result. Dividing by 0, log of 0, tan of 90, that kinda thing.

You can test if your implementation has it:

#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif

The existence of INFINITY is guaranteed by C99 (or the latest draft at least), and "expands to a constant expression of type float representing positive or unsigned infinity, if available; else to a positive constant of type float that overflows at translation time."

NAN may or may not be defined, and "is defined if and only if the implementation supports quiet NaNs for the float type. It expands to a constant expression of type float representing a quiet NaN."

Note that if you're comparing floating point values, and do:

a = NAN;

even then,

a == NAN;

is false. One way to check for NaN would be:

#include <math.h>
if (isnan(a)) { ... }

You can also do: a != a to test if a is NaN.

There is also isfinite(), isinf(), isnormal(), and signbit() macros in math.h in C99.

C99 also has nan functions:

#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);

(Reference: n1256).

Docs INFINITY Docs NAN

This works for both float and double:

double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;

Edit: As someone already said, the old IEEE standard said that such values should raise traps. But the new compilers almost always switch the traps off and return the given values because trapping interferes with error handling.

<inf.h>


/* IEEE positive infinity.  */


#if __GNUC_PREREQ(3,3)
# define INFINITY   (__builtin_inff())
#else
# define INFINITY   HUGE_VALF
#endif

and

<bits/nan.h>
#ifndef _MATH_H
# error "Never use <bits/nan.h> directly; include <math.h> instead."
#endif




/* IEEE Not A Number.  */


#if __GNUC_PREREQ(3,3)


# define NAN    (__builtin_nanf (""))


#elif defined __GNUC__


# define NAN \
(__extension__                                  \
((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; })  \
{ __l: 0x7fc00000UL }).__d)


#else


# include <endian.h>


# if __BYTE_ORDER == __BIG_ENDIAN
#  define __nan_bytes       { 0x7f, 0xc0, 0, 0 }
# endif
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  define __nan_bytes       { 0, 0, 0xc0, 0x7f }
# endif


static union { unsigned char __c[4]; float __d; } __nan_union
__attribute_used__ = { __nan_bytes };
# define NAN    (__nan_union.__d)


#endif  /* GCC.  */

I usually use

#define INFINITY (1e999)

or

const double INFINITY = 1e999

which works at least in IEEE 754 contexts because the highest representable double value is roughly 1e308. 1e309 would work just as well, as would 1e99999, but three nines is sufficient and memorable. Since this is either a double literal (in the #define case) or an actual Inf value, it will remain infinite even if you're using 128-bit (“long double”) floats.

double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);

Here is a simple way to define those constants, and I'm pretty sure it's portable:

const double inf = 1.0/0.0;
const double nan = 0.0/0.0;

When I run this code:

printf("inf  = %f\n", inf);
printf("-inf = %f\n", -inf);
printf("nan  = %f\n", nan);
printf("-nan = %f\n", -nan);

I get:

inf  = inf
-inf = -inf
nan  = -nan
-nan = nan