【问题标题】:How people check nan and inf in C89人们如何在 C89 中检查 nan 和 inf
【发布时间】:2020-05-04 22:22:19
【问题描述】:

isnan(), isinf() 直到 C99 才出现在规范中,有没有在 C89 中实现这样的功能?

我可以使用if (d * 0 != 0) 来检查 d 是 NaN 还是 Inf,但我们总是使用选项 -Werror=float-equal 编译我们的项目,它想大声喊出:error: no == or != on float point value

那么人们如何在 C89 中检查 nan 和 inf 呢?

【问题讨论】:

  • 尝试x > DBL_MAX 获得无穷大,x != x 获得 NAN。
  • C99 已经推出 20 多年了。为什么对 C89 感兴趣?
  • @chux 我认为您的第一条评论可能会成为一个有趣问题的一个很好的答案,并提供一些解释。我无法提供很好的解释,所以我可以请您创建一个答案吗?
  • @chux-ReinstateMonica 直到大约 5 年前,我才专业地使用 C89。这是一个在稍微不寻常的环境中使用的编译器的面向目的的资格和许可问题。还有一些关于人们所依赖的代码行为的担忧。所有这些都带有一丝并非完全不合理的偏执狂。
  • @Yunnosch 已发布答案。 not entirely unreasonable paranoia

标签: c floating-point nan c99 c89


【解决方案1】:

C99 起

isinf(x)isnan(x) 作用于xreal-floating 类型,对于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; }

请注意,C 并不要求实现支持infinity。如果是这样,上述情况永远不会正确。

伊斯南

C89 isnan() 的“自己动手”比较棘手。可以在下面作为函数或简单的宏来完成。功能依赖于现代的非数字行为,其中 Nan 永远不等于任何东西,甚至不等于它自己。 not 由 C89 指定,但通常底层浮点系统遵循这一点。否则,您需要更特定于平台的方法。

/* 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; }

请注意,C 不要求实现支持Not-a-number。如果是这样,上面的内容永远不会正确。

鉴于 C89 的狂野西部时代,我不会假设符合 IEEE 754。 InfinityNAN 正是任何缺乏正式合规性的浮点实现的边缘。祝你好运。


C 允许使用 FP 更广泛的数学运算,具体取决于 FLT_EVAL_METHOD,因此 1.0f / 7.0f 可能会使用 double。这让事情有点复杂,但使用真正的函数确实会将x 表达式强制转换为所需的类型。

【讨论】:

  • 你还好吗? $include 和未完成的 cmets?
【解决方案2】:

如果您的系统使用IEEE 754 Standard 来表示浮点值(大多数情况下),那么您可以显式检查NaNinf 值。单精度(32 位)浮点值具有 1 符号位(位 31)、8 bit 指数(位 30-23)和23 bit 尾数(位 22-0),布局如下(二进制格式):

SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM

infinity 值用全 1 的指数和全 0 的尾数表示(符号位区分负无穷和正无穷)。

NaN 值由全 1 和非零小数的指数表示(安静的 NaN 具有最高有效尾数位 set em>,而 发信号的 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 );
}

双精度值的表示类似,但使用11 指数位 (62-52) 和 52 尾数位 (51-0)。

【讨论】:

  • unsigned int mask = *(unsigned int *)(&amp;test); 违反了严格的别名,ignores any potential endianness issues,并假定 float 的所有位完全适合 unsigned int
  • @AndrewHenle ,你的意思是如果浮点数在我们的硬件中以大端形式表示,这两个函数将无法正常工作?
【解决方案3】:

我想通过逻辑考虑尝试公开 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 "nan 不比较等于、小于或大于任何东西" C89 未指定。那是一个 IEEE 754 规范。这是一个好的开始,但由于 OP 想要 C89 解决方案,nan 更有可能没有遵循今天(2020 年)的预期做法。 OP 可能需要特定于实现的解决方案。
猜你喜欢
  • 1970-01-01
  • 2010-12-27
  • 2017-12-05
  • 2019-01-24
  • 2014-05-03
  • 2018-01-26
  • 2015-09-23
  • 2011-11-23
  • 2012-11-08
相关资源
最近更新 更多