我遇到了一个计算 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/