我正在研究一些Java Math函数的 native C源代码。尤其是tanh()
,我很想知道他们是如何实现的。
但是,what I found让我感到惊讶:
double tanh(double x) {
...
if (ix < 0x40360000) { /* |x|<22 */
if (ix<0x3c800000) /* |x|<2**-55 */
return x*(one+x); /* tanh(small) = small */
...
}
如注释所示,taylor series of tanh(x) around 0开头为:
tanh(x) = x - x^3/3 + ...
那么为什么看起来像他们那样实现它:
tanh(x) = x * (1 + x)
= x + x^2
显然,这不是正确的扩展,甚至比仅使用
tanh(x) = x
(这会更快)的近似值还要差,如下图所示:(粗线是在顶部指示的线。另一条灰色是
log(abs(x(1+x) - tanh(x)))
。Sig当然是tanh(x)
本身。)那么,这是实现中的错误,还是解决某些问题(例如数字问题,我真的没想到)的破解工具?请注意,我希望两种方法的结果都完全相同,因为对于x
编辑:I will include a link to the version of the code at the time of writing, for future reference, as this might get fixed.
最佳答案
在执行该代码的条件下,并假设正在使用IEEE-754 double 浮点表示和算术,1.0 + x
将始终求值为1.0
,因此x * (1.0 + x)
始终将求值为x
。像执行此操作一样,从外部(对于函数)可观察到的唯一效果(不仅仅是返回x
)将是设置IEEE“inexact”状态标志。
尽管我不知道从Java查询FP状态标志的方法,但可以想象其他 native 代码也可以查询它们。但是,很可能通过the Javadocs for java.StrictMath
中的这些注释来给出实现的实际原因:
(添加了强调。)您将在C源代码中注意到一个#include "fdlibm.h"
,它似乎将其与Javadoc注释联系在一起。
关于Java/C : OpenJDK native tanh() implementation wrong?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41818255/