# 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*}
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up