我有一个数值方法,如果出现错误,可以返回nan或inf,出于测试目的,我想暂时强制它返回nan或inf,以确保正确处理情况。是否存在可靠的,独立于编译器的方法来在C中创建nan和inf的值?
谷歌搜索大约10分钟后,我只能找到依赖编译器的解决方案。
Answers:
您可以测试您的实现是否具有:
#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif
INFINITY
C99(或至少是最新的草案)保证了的存在,并且“扩展为表示正或无符号无穷大的float类型的常数表达式,如果可用,则扩展为在转换时溢出的float类型的正常数”。
NAN
可以定义也可以不定义,并且“仅当实现支持浮点类型的静默NaN时才定义。它将扩展为表示静默NaN的float类型的常量表达式。”
请注意,如果要比较浮点值,请执行以下操作:
a = NAN;
即使这样,
a == NAN;
是假的。检查NaN的一种方法是:
#include <math.h>
if (isnan(a)) { ... }
您也可以这样做:a != a
测试是否a
为NaN。
还有isfinite()
,isinf()
,isnormal()
,和signbit()
中宏math.h
在C99。
C99还具有以下nan
功能:
#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);
(参考:n1256)。
a != a
应该永远不会被使用。
a
为非数字时,a == NAN
返回false。IEEE要求它。即使是遵循IEEE的实现,也大多这样做。如果isnan()
未实现,则比直接编写代码更好地包装测试a == NAN
。
没有任何独立于编译器的方式来执行此操作,因为C(也不是C ++)标准都没有说浮点数学类型必须支持NAN或INF。
编辑:我只是检查了C ++标准的措辞,它说这些功能(模板化类numeric_limits的成员):
quiet_NaN()
signalling_NaN()
wiill返回NAN表示“如果可用”。它没有扩展“如果可用”的含义,但是大概类似于“如果实现的FP rep支持它们”。同样,有一个功能:
infinity()
它返回一个正INF表示“如果可用”。
这些都在<limits>
标头中定义-我猜想C标准也有类似的东西(可能也是“如果可用”),但是我没有当前C99标准的副本。
<math.h>
定义nan()
,nanf()
和nanl()
NaN的的那个返回不同表示(作为double
,float
和int
分别地),和无穷大(如果avaliable)可以通过产生一个与被退回log(0)
或东西。即使在C99中,也没有标准的方法来检查它们。的<float.h>
报头(<limits.h>
为整型)是不幸的是沉默inf
和nan
值。
nanl()
返回一个long double
,而不是int
我的评论所说的。我不知道为什么我在打字时没有意识到这一点。
这适用于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的一种简单,可移植的测试可能很有用。
编译器独立的方式,但不是处理器独立的方式来获取这些:
int inf = 0x7F800000;
return *(float*)&inf;
int nan = 0x7F800001;
return *(float*)&nan;
这应该在使用IEEE 754浮点格式(x86可以使用)的任何处理器上工作。
更新:经过测试和更新。
(float &)
?在我看来,这看起来不像C。您需要int i = 0x7F800000; return *(float *)&i;
0x7f800001
是IEEE-754标准中的所谓信令NaN。尽管大多数库和硬件都不支持信号NaN,但最好返回一个安静的NaN,例如0x7fc00000
。
double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);
strtod
并确实转换NaN和Inf。
<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. */
我通常用
#define INFINITY (1e999)
要么
const double INFINITY = 1e999
至少在IEEE 754上下文中有效,因为最高的可表示的double值大致为1e308
。1e309
会和一样好用,1e99999
但是三个九就足够了并且令人难忘。由于这是双精度字面量(在这种#define
情况下)或实际Inf
值,因此即使您使用128位(“长双精度数”)浮点数,它也将保持无限。
1e999
文字也不再四舍五入+Infinity
。根据墨菲定律,这破坏了算法。更糟糕的是,执行“ 128位”构建的人类程序员不可能提前发现该错误。即,很可能发现并识别此错误将为时已晚。非常危险。
这是定义这些常量的一种简单方法,我很确定它是可移植的:
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