c - (1.0e300 + pow(2.0, -30.0) > 1.0) 在 STDC 中究竟做了什么?

标签 c ieee-754 atan2

我遇到了一个计算 atan(x) 的函数(来源是 here )。将其简化为我的问题的核心并稍微重新格式化,他们有类似的东西:

static const double one   = 1.0,
                   huge   = 1.0e300;

double atan(double x)
{
  /* A lot of uninteresting stuff here */

  if (ix < 0x3fdc0000) {              /* |x| < 0.4375 */

    if (ix < 0x3e200000) {            /* |x| < 2^-29 */
      if ((huge + x) > one) return x; /* raise inexact */
    }

    id = -1;
  }

  /* A lot of more uninteresting stuff */
}

我会对if ((huge + x) ...这行很感兴趣应该做什么以及它是如何工作的。

根据评论,如果x的绝对值小于 2^-29 ,表达式或比较会引发 inexact错误。

我的第一个问题是我目前不明白为什么这样做:如果计算 arctan如果 x 的绝对值,则使用该函数会导致不精确的结果太小了,他们为什么不直接使用类似 if (fabs(x) < [some_value_here]) ... 的东西呢? ?我怀疑这只是因为 inexact在他们的硬件/库中不会以这种方式发出警告,但我想确定。

假设我是对的,我的第二个问题是我不明白为什么需要比较。我认为这里的关键点是,一个非常小的数字被添加到一个非常大的数字中,以至于这种添加并没有充分地改变大的数字,甚至根本没有改变。因此,添加将提高 inexact。警告,不是比较。所以我问自己比较应该做什么。这只是为了强制编译器实际计算 (huge + x) ,否则可能会被优化掉?

最后,如果有人能稍微解释一下数学,我将不胜感激。选择值 1.0e300对于 huge似乎是一个相当随意的选择。但这只是一个额外的问题,因为我承认我还没有完成作业中的数学部分(关于 double 值及其 IEEE754 表示,我不是一个新手,但理解数学这段代码的各个方面将花费我一些时间,除非有人给出简短的解释)。

编辑 1

偶然看到的:

float32该函数的版本,包括上面讨论的奇怪的行,几乎字面上仍在 glibc 2.19 中!由于 glibc 旨在可移植,因此该代码也应该如此。它在子目录中 sysdeps\ieee754\flt-32 , 所以我想这是 float32 的软件模拟功能,其中可移植性不是问题,因为不会出现依赖于硬件的奇怪现象(我认为软件仿真会引发这些异常,完全符合 IEEE754 中的定义)。

最佳答案

if ((huge + x) > one) return x;的意图就是产生一个浮点不精确的异常,然后从例程中返回。

浮点异常不是陷阱或处理器异常。这只是意味着在浮点运算中发生了一些不寻常的事情。然后会发生什么取决于操作的情况。特别是,可以设置浮点环境,以便不精确的异常仅在特殊寄存器中引发一个标志并继续操作,提供数字结果。或者它可能被设置为一个不准确的异常导致一个陷阱,并且程序控制被重定向到一个陷阱处理程序。

此代码实现 atan不知道浮点环境是如何设置的。也许它可以获取设置,但它不想为此烦恼。鉴于它已决定不能精确计算反正切函数,触发浮点不精确异常的最简单方法只是执行一个简单的加法,但结果不精确。这种不精确的加法将具有与不精确的反正切所需的相同的行为——它要么只是升起标志,要么导致陷阱,具体取决于设置。

至于为什么要和ix < 0x3e200000做比较,这个不清楚。一方面,ix已进行调整以反射(reflect)绝对值,而 x没有,为什么不使用已经准备好的ix而不是使用另一个操作来生成 fabs(x) ?此外,整数比较通常比浮点比较占用更少的处理器资源,尤其是在编写此代码时的处理器中。或者可能是作者只是碰巧在作者之上使用了一个,也许他们的大部分代码都是使用 ix 编写的对浮点编码进行操作而不是 x对浮点值进行操作,他们不想不必要地来回切换。也可能是因为代码是在十六进制浮点符号可用之前编写的(所以我们可以写 x < 0x1p-29f ),编译器不擅长将十进制数字转换为浮点值,所以他们不想写在源代码中输出浮点值。

这种代码是有问题的,并且高度依赖于它所针对的 C 实现。通常,编译器可能无法保证 (huge + x) > one将在程序执行期间进行评估。编译器可能会在编译时评估它。不过,据推测,如果此代码是为特定的 C 实现编写的,他们知道编译器将在编译时对其进行评估,或者将确保获得相同的结果,包括引发浮点不精确异常。

从表面上看,(huge + x) > one似乎没有做任何有用的事情 huge + x单独做不到,但也许作者知道一些我们不了解的编译器。

huge不需要是 1.0e300 .任何大到 huge 之和的值和 x不能精确就足够了。

关于c - (1.0e300 + pow(2.0, -30.0) > 1.0) 在 STDC 中究竟做了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54773111/

相关文章:

c - 如何将 C 结构体与 4 字节边界对齐?

opengl - OpenGL GLSL中的atan(y/x)和atan2(y,x)有什么区别

c - 指针减法给出奇怪的结果

c - 图片加载、C语言和GTK

floating-point - 为什么 IEEE 754 保留这么多 NaN 值?

algorithm - 计算正确舍入/几乎正确舍入的浮点立方根

c - atan2 用于任意相移的两个正弦波?

mysql - 在MySQL中计算风向的平均值

c - 每 n 毫秒写入一个字符串

floating-point - 微小 "before rounding"(而不是 "after rounding")的理由是什么?