如何在C中使用nan和inf?


89

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

谷歌搜索大约10分钟后,我只能找到依赖编译器的解决方案。


C标准未定义浮点数。因此,没有任何独立于编译器的方式可以执行所需的操作。
约翰·科特林斯基

Answers:


86

您可以测试您的实现是否具有:

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

INFINITYC99(或至少是最新的草案)保证了的存在,并且“扩展为表示正或无符号无穷大的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)。

文件INFINITY 文件NAN


2
极好的答案。参考的NAN和INFINITY宏是C99§7.12第4和第5,除了(isnan(A)),你也可以使用(A = A!)对C.符合标准的实施检查NaN
斯蒂芬佳能

23
出于可读性的爱,a != a应该永远不会被使用。
克里斯·凯雷克斯

1
@ChrisKerekes:可悲的是,我们中有些人有NAN,但没有isnan()。是的,这是2017年。:(
eff

C不需要,什么时候 a为非数字时,a == NAN返回false。IEEE要求它。即使是遵循IEEE的实现,也大多这样做。如果isnan()未实现,则比直接编写代码更好地包装测试a == NAN
chux-恢复莫妮卡

34

没有任何独立于编译器的方式来执行此操作,因为C(也不是C ++)标准都没有说浮点数学类型必须支持NAN或INF。

编辑:我只是检查了C ++标准的措辞,它说这些功能(模板化类numeric_limits的成员):

quiet_NaN() 
signalling_NaN()

wiill返回NAN表示“如果可用”。它没有扩展“如果可用”的含义,但是大概类似于“如果实现的FP rep支持它们”。同样,有一个功能:

infinity() 

它返回一个正INF表示“如果可用”。

这些都在<limits>标头中定义-我猜想C标准也有类似的东西(可能也是“如果可用”),但是我没有当前C99标准的副本。


令人失望和惊讶。C和C ++是否不符合IEEE浮点数,它们具有nan和inf的标准表示形式?
图形菜鸟

14
在C99,C头<math.h>定义nan()nanf()nanl()NaN的的那个返回不同表示(作为doublefloatint分别地),和无穷大(如果avaliable)可以通过产生一个与被退回log(0)或东西。即使在C99中,也没有标准的方法来检查它们。的<float.h>报头(<limits.h>为整型)是不幸的是沉默infnan值。
克里斯·卢茨

哇,真是个大麻烦。nanl()返回一个long double,而不是int我的评论所说的。我不知道为什么我在打字时没有意识到这一点。
克里斯·卢兹

@Chris,请参阅我对C99的回答。
Alok Singhal

2
@IngeHenriksen-可以肯定地说,微软已经声明它不打算使用VC ++支持C99。
克里斯·卢茨

24

这适用于floatdouble

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

编辑:正如某人已经说过的那样,旧的IEEE标准说这样的值应该引起陷阱。但是新的编译器几乎总是关闭陷阱并返回给定的值,因为陷阱会干扰错误处理。


陷阱是754-1985下允许的错误处理的一种选择。大多数现代硬件/编译器使用的行为也被允许(这是委员会许多成员的首选行为)。许多实现者错误地认为,由于不幸地在标准中使用了“例外”一词,因此需要进行陷阱。修订后的754-2008中对此做了很大的澄清。
斯蒂芬·佳能

嗨,斯蒂芬,你是对的,但是标准还说:“用户应该能够通过为其指定处理程序来请求捕获五个异常中的任何一个。他应该能够请求禁用现有的处理程序。 ,保存或恢复。他还应该能够确定是否已为指定的异常启用特定的陷阱处理程序。” 定义为“应该”(2.定义)的意思是“强烈建议”,并且仅当体系结构等使其不可行时,才应省略其实现。80x86完全支持该标准,因此C没有理由不支持它。
Thorsten S.

我同意C应该要求754(2008)浮点数,但是有充分理由不这样做。具体来说,C用于x86以外的所有其他环境-包括不具有硬件浮点数的嵌入式设备,以及程序员甚至不希望使用浮点数的信号处理设备。对与错,这些用法在语言规范中占了很多惯性。
斯蒂芬·佳能

我不知道为什么最重要的答案就在那里。它没有任何方式产生请求的值。这个答案可以。
drysdam

#define is_nan(x) ((x) != (x))作为NAN的一种简单,可移植的测试可能很有用。
鲍勃·斯坦

21

编译器独立的方式,但不是处理器独立的方式来获取这些:

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

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

这应该在使用IEEE 754浮点格式(x86可以使用)的任何处理器上工作。

更新:经过测试和更新。


2
@WaffleMatt-为什么这个端口不在32/64位之间?不管基础处理器的寻址大小如何,IEEE 754单精度浮点数均为32位。
亚伦,

6
投向(float &)?在我看来,这看起来不像C。您需要int i = 0x7F800000; return *(float *)&i;
克里斯·卢茨

6
注意,这0x7f800001是IEEE-754标准中的所谓信令NaN。尽管大多数库和硬件都不支持信号NaN,但最好返回一个安静的NaN,例如0x7fc00000
斯蒂芬·佳能

6
警告:这可能会因违反严格的别名规则而触发未定义行为。推荐的(最好在编译器中得到支持)进行类型修剪的方法是通过union成员
ulidtko 2015年

2
除了@ulidtko指出的严格的别名问题外,它还假定目标对整数使用相同的字节序作为浮点数,这肯定并非总是如此。
mr.stobbe

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

4
这是一个智能的便携式解决方案!C99要求strtod并确实转换NaN和Inf。
ulidtko 2015年

1
这种解决方案并没有缺点。它们不是常数。您不能使用这些值来实例化全局变量(或初始化数组)。
马克

1
@马克 您始终可以拥有一个初始化函数,该函数调用一次并在全局命名空间中进行设置。这是一个非常可行的缺点。
疯狂物理学家

3
<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.  */

0

我也很惊讶这些不是编译时间常数。但是我想您可以通过简单地执行一条返回无效结果的指令来轻松创建这些值。除以0,对数为0,棕褐色为90,这有点。


0

我通常用

#define INFINITY (1e999)

要么

const double INFINITY = 1e999

至少在IEEE 754上下文中有效,因为最高的可表示的double值大致为1e3081e309会和一样好用,1e99999但是三个九就足够了并且令人难忘。由于这是双精度字面量(在这种#define情况下)或实际Inf值,因此即使您使用128位(“长双精度数”)浮点数,它也将保持无限。


1
我认为这非常危险。想象一下有人在20年左右的时间内如何将您的代码迁移到128位浮点数(在您的代码经历了令人难以置信的复杂演变之后,您今天无法预测其中的任何阶段)。突然,指数范围急剧增加,您所有的1e999文字也不再四舍五入+Infinity。根据墨菲定律,这破坏了算法。更糟糕的是,执行“ 128位”构建的人类程序员不可能提前发现该错误。即,很可能发现并识别此错误将为时已晚。非常危险。
ulidtko 2015年

1
当然,以上最坏的情况可能与现实不符。但是,仍然考虑替代方案!最好保持安全。
ulidtko 2015年

2
“ 20年后”,嘿。来吧。这个答案不坏的。
alecov

@ulidtko我也不喜欢这个,但是真的吗?
Iharob Al Asimi

0

这是定义这些常量的一种简单方法,我很确定它是可移植的:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.