(1.0e300 + pow(2.0,-30.0)> 1.0)在STDC中到底能做什么?

时间:2019-02-19 18:51:47

标签: 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似乎是一个任意选择。但这只是一个额外的问题,因为我承认我尚未完成作业的 math 部分(关于double值及其IEEE754表示,我不是一个新手,但了解除非有人简短说明,否则这段代码的数学方面将花费我一些时间。

编辑1

偶然发现:

该函数的float32版本(包括上面讨论的怪异行)几乎仍然在glibc 2.19中!由于glibc是可移植的,因此该代码也应该是可移植的。它位于子目录sysdeps\ieee754\flt-32中,因此我想这是float32函数的软件仿真,其中的可移植性没有问题,因为不会出现与硬件相关的怪异现象(我认为该软件仿真会完全按照IEEE754的定义引发这些异常。

1 个答案:

答案 0 :(得分:7)

if ((huge + x) > one) return x;的目的是生成浮点不精确异常,然后从例程返回。

浮点异常不是陷阱或处理器异常。这仅表示在浮点运算中发生了异常情况。然后,所发生的情况取决于操作的情况。特别是,可以设置浮点环境,以便不精确的异常仅在特殊寄存器中引发一个标志,然后继续该操作,从而提供数值结果。或者可以将其设置为使不精确的异常导致陷阱,并将程序控制重定向到陷阱处理程序。

此实现atan的代码不知道如何设置浮点环境。也许它可以获取设置,但是它不想这么做。既然已经确定不能精确计算反正切函数,那么它触发浮点不精确异常的最简单方法就是执行具有不精确结果的简单加法。这种不精确的加法将具有与不精确的反正切所期望的相同行为-根据设置,它将只是升起标记或导致陷阱。

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

这种代码是有问题的,并且高度依赖于为其编写的C实现。通常,编译器可能无法保证x < 0x1p-29f将在程序执行期间被评估。编译器可能会在编译时对其进行评估。但是,大概可以假设,如果为特定的C实现编写此代码,他们知道编译器将在编译时对其进行评估,或者将确保获得相同的结果,包括引发浮点不精确异常。

表面上,(huge + x) > one似乎并没有做任何(huge + x) > one不能做的有用的事情,但是也许作者对编译器有所了解,而我们却没有。

huge + x不必为huge。任何大到1.0e300huge之和都不能精确的值就足够了。