cmath
计算机是如何实现tanh计算的
一、tanh的一些基本内容
对于tanh函数有公式为 t a n h ( x ) = ( e x − e − x ) / ( e x + e − x ) tanh(x) = (e^{x} -e^{-x}) / (e^{x}+e^{-x}) tanh(x)=(ex−e−x)/(ex+e−x)。其定义域为R,原因是分母利用不等式可得>=2,所以无限制条件。
其次需要知道tanh的泰勒展开式,为
t
a
n
h
(
x
)
=
∑
n
=
1
∞
2
2
n
(
2
n
−
1
)
B
2
n
x
2
n
−
1
(
2
n
!
)
tanh(x) = \sum_{n=1}^{\infty} \frac {2^{2n} (2n-1)B_{2n}x^{2n-1}}{(2n!)}
tanh(x)=n=1∑∞(2n!)22n(2n−1)B2nx2n−1
其中
B
2
n
B_{2n}
B2n是伯努利数。
其函数图像如下:
一目了然,奇函数有$-tanh(x) = tanh(-x) $。因此我们只需要在正区间内讨论其值即可。
另外需要清楚这个导数公式,也可以证出其单调性,以及曲线趋于平直的点 [ t a n h ( x ) ] ′ = 1 − t a n h 2 ( x ) [{tanh(x)}]' = 1-tanh^{2}(x) [tanh(x)]′=1−tanh2(x)
二、代码
1.我的实现例子
代码如下(示例):
#include <bits/stdc++.h>
using namespace std;
float myexp(float x){
return 1+x+x*x/2+x*x*x/6+x*x*x*x/24+x*x*x*x*x*x/120;
}
float mytanh(float x){
int ix = (*(int*)&x) & 0x7fffffff; //去掉符号位
float t; //中间变量
float z; //最终结果
if(ix >= 0x7f800000){
if(x >= 0) return 1; //+Inf
else return 1/x - 1; //-Inf or NaN
}
if(ix < 0x41B00000){ //|x| < 22
if(ix < 0x24000000){ //|x| < -55
return x*(1+x);
}
if(ix >= 0x40000000){ //|x| >= 1
t = myexp(2*ix)-1;
z = 1 - 2/(t+2);
}else{
t = myexp(-2*ix)-1;
z = -t/(t+2);
}
}else{
return z = 1; // |x| > 22 return +-1
}
return (x >= 0)? z : -z;
}
int main(){
float i = 5.123456;
float m = mytanh(i);
float n = tanh(i);
float q = m - n;
cout << "mytanh = " << m << "cmath's tanh " << n << endl;
cout << q;
return 0;
}
2.代码中分段函数的实现原理
1、在
0
<
=
x
<
=
2
−
55
0 <= x <= 2^{-55}
0<=x<=2−55时,有
t
a
n
h
(
x
)
=
x
∗
(
1
+
x
)
tanh(x) = x*(1+x)
tanh(x)=x∗(1+x)
2、在
2
−
55
<
x
<
=
1
2^{-55} < x <=1
2−55<x<=1时, 有
t
a
n
h
(
x
)
=
−
t
t
+
2
tanh(x) = \frac{-t}{t+2}
tanh(x)=t+2−t 其中
t
=
e
−
2
x
−
1
t = e^{-2x}-1
t=e−2x−1
3、在
1
<
=
x
<
22
1 <= x < 22
1<=x<22时,有
t
a
n
h
(
x
)
=
1
−
2
t
+
2
tanh(x) = 1 - \frac{2}{t+2}
tanh(x)=1−t+22 其中
t
=
e
2
x
−
1
t = e^{2x}-1
t=e2x−1
4、在
22
<
x
<
i
n
f
22 < x < inf
22<x<inf时,有
t
a
n
h
(
x
)
=
1
tanh(x) = 1
tanh(x)=1
下面对这四段函数进行具体解释:
对于第一段,是因为在x趋于一个很小的数值时近似有Tanh(x) = x,此时(1+x)约等于1,对于公式中逼近0的一段区间分子使用泰勒即为
2
x
+
2
x
2
2x+2x^{2}
2x+2x2,分母就是2,所以可以直接得到x(1+x)这个式子。但是为什么是
2
−
55
2^{-55}
2−55这个值,还有待研究,但是确定的是这里使用一个较小的值即可
对于第二和第三段,对tanh(x)的公式进行通分化简即可得出结果,无误差。
对于第四段,便可以使用我们前边提到过的导数,令导数为0,也即求 t a n h ( x ) = 1 tanh(x) = 1 tanh(x)=1的x的值,在计算器中我们可以得到tanh(17) 约等于 0.9999999999999964。tanh(18)约等于1,因此找到了一个点,但是在glibc中其使用了22作为这个点,大于这个点的x代入tanh(x)中均会得到结果1。
补充
代码中使用了位运算的技巧,需要清楚ieee754浮点数标准后进行阅读。这篇只是对tanh标量的初步认知,后面重点是进行向量化计算。