# fast_tanh 疑問
> [2025-03-18 問答筆記](https://hackmd.io/EFs_Nfs6TOmIA5ldA_tZsQ?view#EricccTaiwan)
```c=
float fast_tanh(float v)
{
/* Constants for polynomial approximation */
const float c1 = 0.03138777f;
const float c2 = 0.276281267f;
const float log2_e = 1.442695022f; /* log2(e) */
/* Scale input by log2(e) for exponentiation base 2 */
v *= log2_e;
/* Separate integer and fractional parts */
int int_part = (int) v;
float frac_part = v - int_part;
/* Compute polynomial approximation terms */
float frac_part_sq = frac_part * frac_part;
float exp_frac_approx = log2_e + c2 * frac_part_sq;
float exp_frac_linear = frac_part + (c1 * frac_part_sq * frac_part);
float exp_approx = exp_frac_linear + exp_frac_approx;
/* Efficiently compute scaling by 2^(int_part) */
union {
float f; // holds the actual floating-point value
uint32_t i; // used for manipulating the IEEE 754 bit representation
} exp_scaled = {.f = exp_approx};
exp_scaled.i += (uint32_t) (int_part << 23);
float exp_neg_approx = exp_frac_linear - exp_frac_approx;
/* Combine terms to approximate tanh(v) */
return (exp_scaled.f + exp_neg_approx) / (exp_scaled.f - exp_neg_approx);
}
```
### Q1
這邊計算小數部份, lin 8 ~ 11
$$
2^{\text{frac_part}} \approx log_2e + 1\times\text{frac_part}+\text{c2}\times\text{frac_part}^2 + c1 \times \text{frac_part}^3
$$
但這跟實作 Minimax approximation algorithm 近似法去求 `c1` 和 `c2` 不是不一樣嘛
$$
2^{x} \approx 1 + \ln2\times x+\text{c2}\times x^2 + c1 \times x^3
$$
單論,0 和 1 次項的係數就差了 $\ln2$ ,`c1`和`c2`甚至可以共用,不太理解?
> 對的,就是差 $\ln2$ 倍數,用 jenkas 的係數所算出來的 $e^{0.7} = 2.905217$ 剛好與實際值 $e^{0.7}= 2.013752707$ 相差 $\ln2$ 倍數
### Q2
為何 `exp_neg_approx` 計算 ($e^{-x}$),不需要做整數的縮放,只有計算小數部份而已 (line 30)?
### Q3
`exp_frac_approx` 和 `exp_frac_linear` 改成 `exp_frac_even_term` 和 `exp_frac_odd_term` 是否更符合泰勒展開的意思呢?
### Q4
`fast_tanh()` 也有使用到浮點數運算,所以與理想上的定點數操作,還有段改進空間?
### 處理 $e^{-x}$
```c
#include <stdio.h>
#include <stdint.h>
float fast_tanh(float v)
{
/* Constants for polynomial approximation */
const float c1 = 0.03138777f;
const float c2 = 0.276281267f;
const float log2_e = 1.442695022f; /* log2(e) */
/* Scale input by log2(e) for exponentiation base 2 */
float v_pos = v * log2_e;
float v_neg = -v_pos;
// ------- exp(+v) -------
int int_part_pos = (int)v_pos;
float frac_part_pos = v_pos - int_part_pos;
float frac_sq_pos = frac_part_pos * frac_part_pos;
float even_pos = log2_e + c2 * frac_sq_pos;
float odd_pos = frac_part_pos + c1 * frac_sq_pos * frac_part_pos;
float approx_pos = even_pos + odd_pos;
union {
float f;
uint32_t i;
} exp_pos = {.f = approx_pos};
exp_pos.i += (uint32_t)(int_part_pos << 23);
// ------- exp(-v) -------
int int_part_neg = (int)v_neg;
float approx_neg = even_pos - odd_pos;
union {
float f;
uint32_t i;
} exp_neg = {.f = approx_neg};
exp_neg.i += (uint32_t)(int_part_neg << 23);
// ------- 結果 -------
printf("exp(+v) = %.6f\n", exp_pos.f);
printf("exp(-v) = %.6f\n", exp_neg.f);
return (exp_pos.f - exp_neg.f) / (exp_pos.f + exp_neg.f);
}
int main()
{
float v = 0.7f ;
float result = fast_tanh(v);
printf("hi\n");
printf("fast_tanh(%.2f) = %.6f\n", v, result);
return 0;
}
```
\begin{align*}
\frac{e^{2x}-1}{e^{-2x}+1}
&= \frac{1 + (\ln 2)(2x) + \frac{(\ln 2)^2}{2}(2x)^2 + \frac{(\ln 2)^3}{6}(2x)^3 - 1}
{1 + (\ln 2)(-2x) + \frac{(\ln 2)^2}{2}(-2x)^2 + \frac{(\ln 2)^3}{6}(-2x)^3 + 1} \\
&= \frac{(\ln 2)(2x) + 2 (\ln 2)^2 x^2 +\frac{4(\ln 2)^3}{3}x^3}{2 - (\ln2) (2x)+2(\ln2)^2x^2+\frac{4(\ln)^3}{3}x^3} \\
&= q
\end{align*}