isnan(),isinf()
函数并不存在。那么在C89中有没有实现这样的功能的方法呢?可以使用if (d * 0 != 0)
来检查d是否为NaN或Inf,但我们总是使用选项-Werror=float-equal
编译我们的项目,这会提示错误:error: no == or != on float point value
。那么人们如何在C89中检查NaN和Inf呢?isnan(),isinf()
函数并不存在。那么在C89中有没有实现这样的功能的方法呢?可以使用if (d * 0 != 0)
来检查d是否为NaN或Inf,但我们总是使用选项-Werror=float-equal
编译我们的项目,这会提示错误:error: no == or != on float point value
。那么人们如何在C89中检查NaN和Inf呢?自C99起
isinf(x)
和isnan(x)
适用于x
的实浮点类型,对于其他类型如int
则未定义。 isinf(x)
和 isnan(x)
是宏定义。
isinf(x)
和isnan(x)
对float,double,long double
使用相同的名称,并且就像重载函数一样。
isinf
对于C89, 我们可以使用单独的函数来测试_MAX
。请注意,C89未定义long double
。
"自制"的isinf()
可以使用以下内容。
#include <float.h>
int isinf_f(float x) { return x < -FLT_MAX || x > FLT_MAX; }
int isinf_d(double x) { return x < -DBL_MAX || x > DBL_MAX; }
/* Note `x` used twice here - so use with caution */
#define my_is_nan(x) ( (x) != (x) )
int isnan_f(float x) { x != x; }
int isnan_d(double x) { x != x; }
如果您的系统使用 IEEE 754标准来表示浮点数值(大多数情况下),那么您可以明确检查NaN
和inf
值。单精度(32位)浮点值具有1
符号位(第31位),8位
指数(位30-23)和23位
尾数(位22-0),排列如下(以二进制格式):
SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
一个无穷大的值用所有1的指数和所有0的尾数表示(符号位区分负无穷大和正无穷大)。
NaN值由所有1的指数和非零分数表示(quiet NaN具有最高有效尾数位设置,而signalling NaN则清除此位)。
因此,通过将float
强制转换为无符号32位整数,我们可以明确地检查这些表示(假设unsigned int
是32位类型):
int IsInfinite(float test) {
unsigned int mask = *(unsigned int *)(&test);
return ( (mask & 0x7F800000) == 0x7F800000 && (mask & 0x007FFFFF) == 0 );
}
int NotANumber(float test) {
unsigned int mask = *(unsigned int *)(&test);
return ( (mask & 0x7F800000) == 0x7F800000 && (mask & 0x007FFFFF) != 0 );
}
unsigned int mask = *(unsigned int *)(&test);
违反了 strict aliasing 规则,忽略了任何可能的字节序问题,并假设浮点数 float
的所有位都恰好适合于一个 unsigned int
。 - Andrew Henle经过审核,C89也许可以采用另一种方法。
如果我们假设将NaN与有限值进行比较始终为false(这是IEEE的方法),我们可以猜测在不遵循该方法的系统中,比较始终为true(而不是有时为true)。然后,关键是处理这两种情况。
对于非NaN的x
,(x >= 0.0) == (x < 0.0)
应该总是false,因为从数学上讲它们是相反的测试。
对于NaN的x
,如果我们幸运地发现这样的x
与常量的比较始终为false或始终为true,则is_nan()
的结果为true。
// Assuming -Werror=float-equal
int is_nan(double x) {
return (x >= 0.0) == (x < 0.0);
}
static_assert()
。由于C89中不存在_Static_assert
,因此请考虑使用此处找到的各种C89替代品。我想尝试通过逻辑推理来揭示NaN。
如果一个float
既不是无穷大(既不大于FLT_MAX
也不小于-FLT_MAX
),那么它应该小于或等于FLT_MAX
。如果不是,那么它就是NaN。
int is_inf(float x) { return x < -FLT_MAX || x > FLT_MAX; }
int is_nan(float x) { return !is_inf(x) && !(x <= FLT_MAX); }
这是我的第一个回答,却已经被踩了。所以我决定加上一个例子。好吧,它按预期工作。
#include <stdio.h>
#include <math.h>
#include <float.h>
static int is_inf(float x) { return x < -FLT_MAX || x > FLT_MAX; }
static int is_nan(float x) { return !is_inf(x) && !(x <= FLT_MAX); }
static void show(float f)
{
float g = -f;
printf("% f : %i %i %i %i\n", f, isinf(f), isnan(f), is_inf(f), is_nan(f));
printf("% f : %i %i %i %i\n", g, isinf(g), isnan(g), is_inf(g), is_nan(g));
}
int main(void)
{
float inf = FLT_MAX * 2.0f;
float nan = inf / inf;
show(inf);
show(nan);
show(FLT_MAX);
show(FLT_MIN);
show(0.0f);
show(1234.5678f);
return 0;
}
在Linux下编译并启动:
$ gcc infnan.c
$ ./a.out
inf : 1 0 1 0
-inf : -1 0 1 0
-nan : 0 1 0 1
nan : 0 1 0 1
340282346638528859811704183484516925440.000000 : 0 0 0 0
-340282346638528859811704183484516925440.000000 : 0 0 0 0
0.000000 : 0 0 0 0
-0.000000 : 0 0 0 0
0.000000 : 0 0 0 0
-0.000000 : 0 0 0 0
1234.567749 : 0 0 0 0
-1234.567749 : 0 0 0 0
提供对数据类型double
的补充解决方案,改进了返回值。
static int is_inf(double x) {
if (x > DBL_MAX) return 1;
if (x < -DBL_MAX) return -1;
return 0;
}
static int is_nan(double x) { return !is_inf(x) && !(x <= DBL_MAX); }
is_nan
可以更容易地计算。int is_nan(double x) { return !(x > DBL_MAX || x <= DBL_MAX); }
或者甚至可以不包括常量。
int is_nan(double x) { return !(x > 0.0 || x <= 0.0); }
nan
不等于、小于或大于任何东西。 - S.S. Anne"%-14g"
代替"%f"
以获得更干净、更具信息性的表格。 - chux - Reinstate Monica(x<C)==(x>=C)
(两者都为false)。这个建议碰巧使用C=FLT_MAX
作为要比较的常量,这很好。至于非IEEE754答案,那并没有太多意义。NaN非常符合IEEE754的发明,尤其是非信号NaN。 - MSalters
x > DBL_MAX
表示无穷大,使用x != x
表示 NAN。 - chux - Reinstate Monica-Werror=float-equal
。 - hukeping