我遇到了一个计算atan(x)
的函数(源代码在这里)。简化问题并稍微重新格式化一下,他们有类似下面的代码:
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 中定义的完全引发这些异常)。
2^-30
暗示了对 2 和 -30 进行按位异或(尽管由于 C++ 的常常令人惊讶的运算符优先级,这并不是实际情况)。 :-) - Adrian McCarthy