我有一种数值方法,如果出现错误,它可能会返回nan或inf,为了测试目的,我想临时强制它返回nan或inf,以确保情况被正确处理。在C语言中,是否有一种可靠的、独立于编译器的方法来创建nan和inf的值?
经过大约10分钟的谷歌搜索,我只能找到与编译器相关的解决方案。
你可以测试一下你的实现是否具备它:
#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif
C99(或最新的草案)保证了INFINITY
的存在,“如果可用,则扩展为表示正无穷大或无符号无穷大的float类型的常量表达式;否则,扩展为在翻译时溢出的float类型的正常量。”
NAN
可能被定义或未被定义,“当且仅当实现支持float类型的quiet NaN时才定义。它扩展为表示静默NaN的float类型的常量表达式。”
请注意,如果您正在比较浮点值,并进行以下操作:
a = NAN;
即便如此,
a == NAN;
为false。检查NaN的一种方法是:
#include <math.h>
if (isnan(a)) { ... }
您还可以使用 a != a
来测试 a
是否为 NaN。
C99 中的 math.h
还有一些宏,如 isfinite()
、isinf()
、isnormal()
和 signbit()
。
C99 还提供了 nan
函数:
#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);
(参考:n1256)。
a != a
绝对不能使用。 - Chris Kerekesa != a
在两个不同的优化级别下可能会有不同的行为。请谨慎使用此比较! - Will Eccles没有编译器独立的方法来实现这一点,因为C(或C++)标准都没有规定浮点数类型必须支持NAN或INF。
编辑:我刚刚检查了C++标准的措辞,它说这些函数(模板类numeric_limits的成员):
quiet_NaN()
signalling_NaN()
将返回NAN表示“如果可用”。它没有详细说明“如果可用”的含义,但可能是指“如果实现的FP表示支持它们”。同样,还有一个函数:
infinity()
它会返回一个正的 INF 表示“如果可用”。
这两个值在 <limits>
头文件中定义 - 我猜测 C 标准也有类似的东西(可能也是“如果可用”),但我没有当前 C99 标准的副本。
<math.h>
定义了 nan()
、nanf()
和 nanl()
三个函数来返回不同表示的 NaN(分别作为 double
,float
和 int
)。如果可用,通过使用类似 log(0)
的方法可以返回无穷大。即使在 C99 中,也没有标准的方法来检查它们。很遗憾的是,C 标准头文件 <float.h>
(对于整型类型则是 <limits.h>
)没有提及 inf
和 nan
值。 - Chris Lutznanl()
返回的是long double
,而不是像我的评论所说的int
。我不知道为什么在打字时没有意识到这一点。 - Chris Lutzmath.h
中。但是在limits.h
中有一些,请参见http://msdn.microsoft.com/en-us/library/6hthw3cb(v=vs.110).aspx。例如:`float n = numeric_limits<float>::infinity();` - user152949这适用于float
和double
:
double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;
编辑: 正如其他人所说,旧的IEEE标准规定这些值应该引发陷阱。但是,新编译器几乎总是关闭陷阱并返回给定值,因为陷阱会干扰错误处理。
#define is_nan(x) ((x) != (x))
可以作为一个简单、便携的测试 NAN 的方法。 - Bob Steindouble a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);
strtod
函数并能够转换NaN和Inf。 - ulidtko获取这些的方法不依赖于编译器,但是与处理器有关:
int inf = 0x7F800000;
return *(float*)&inf;
int nan = 0x7F800001;
return *(float*)&nan;
这应该适用于使用 IEEE 754 浮点格式的任何处理器(x86 使用该格式)。
更新:已测试并更新。
(float &)
作为类型转换?这不像是 C 语言的写法。你需要使用int i = 0x7F800000; return *(float *)&i;
来进行操作。 - Chris Lutz0x7f800001
是 IEEE-754 标准中所谓的“信号型” NaN。尽管大多数库和硬件不支持信号型 NaN,但最好返回一个静默 NaN,如 0x7fc00000
。 - Stephen Canon<inf.h>
/* IEEE positive infinity. */
#if __GNUC_PREREQ(3,3)
# define INFINITY (__builtin_inff())
#else
# define INFINITY HUGE_VALF
#endif
并且
<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. */
这里有一种简单的方法来定义那些常量,我相信它是可移植的:
const double inf = 1.0/0.0;
const double nan = 0.0/0.0;
printf("inf = %f\n", inf);
printf("-inf = %f\n", -inf);
printf("nan = %f\n", nan);
printf("-nan = %f\n", -nan);
inf = inf
-inf = -inf
nan = -nan
-nan = nan
code
INFINITY = HUGE_VALF = 1e10000fcode
NAN = (0.0f / 0.0f)无论如何,看起来 INFINITY 和 NAN 都被定义为固定常量。 - Patrick Chkoreffcorrect_math.h
中定义,在math.h
中包含:#ifndef _HUGE_ENUF
#define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow
#endif
#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
#define HUGE_VAL ((double)INFINITY)
#define HUGE_VALF ((float)INFINITY)
#define HUGE_VALL ((long double)INFINITY)
#ifndef _UCRT_NEGATIVE_NAN
// This operation creates a negative NAN adding a - to make it positive
#define NAN (-(float)(INFINITY * 0.0F))
#else
// Keep this for backwards compatibility
#define NAN ((float)(INFINITY * 0.0F))
#endif
我通常使用
#define INFINITY (1e999)
或者
const double INFINITY = 1e999
这个方法至少在IEEE 754的上下文中有效,因为最高可表示的双精度浮点数大约是1e308
。1e309
同样有效,1e99999
也一样,但三个九已经足够且容易记忆。由于这要么是一个双精度字面量(在#define
的情况下),要么是一个实际的Inf
值,即使你使用128位(“长双精度”)浮点数,它仍将保持无穷。
1e999
文本不再四舍五入为+Infinity
。按照墨菲定律,这将破坏算法。更糟糕的是:做“128位”编译的人工程序员很可能事先没有注意到这个错误。也就是说,当发现和识别这个错误时,很可能已经太晚了。非常危险。 - ulidtko我也很惊讶这些不是编译时常量。但我想你可以通过执行返回无效结果的指令来轻松创建这些值。比如除以0,log(0),tan(90)之类的东西。