| 89 | } |
| 90 | |
| 91 | void tanh_kernel(int i, int id, void* data, const float* input, float* output) |
| 92 | { |
| 93 | int step = ((int*)data)[0]; |
| 94 | float32x4_t min = vdupq_n_f32(-30.0f); |
| 95 | float32x4_t max = vdupq_n_f32(30.0f); |
| 96 | const float* cur_input = input + id * step; |
| 97 | float* cur_output = output + id * step; |
| 98 | for(int i = 0; i < (step & -4); i += 4) |
| 99 | { |
| 100 | float32x4_t _input = vld1q_f32(cur_input); |
| 101 | _input = vmaxq_f32(_input, min); |
| 102 | _input = vminq_f32(_input, max); |
| 103 | /// float32x4_t positive_exp = vexpq10_f32(_input); |
| 104 | /// float32x4_t negative_exp = vexpq10_f32(vmulq_n_f32(_input, -1.0f)); |
| 105 | float32x4_t denominator = vaddq_f32(vexpq10_f32(_input), vexpq10_f32(vmulq_n_f32(_input, -1.0f))); |
| 106 | float32x4_t numerator = vsubq_f32(vexpq10_f32(_input), vexpq10_f32(vmulq_n_f32(_input, -1.0f))); |
| 107 | |
| 108 | float32x4_t tmp_recip = vrecpeq_f32(denominator); |
| 109 | tmp_recip = vmulq_f32(vrecpsq_f32(denominator, tmp_recip), tmp_recip); |
| 110 | tmp_recip = vmulq_f32(vrecpsq_f32(denominator, tmp_recip), tmp_recip); |
| 111 | float32x4_t out = vmulq_f32(numerator, tmp_recip); |
| 112 | vst1q_f32(cur_output, out); |
| 113 | cur_input += 4; |
| 114 | cur_output += 4; |
| 115 | } |
| 116 | for(int i = step & ~3; i < step; i++) |
| 117 | { |
| 118 | float tmp = *input++; |
| 119 | tmp = T_MIN(tmp, 30.0f); |
| 120 | tmp = T_MAX(tmp, -30.0f); |
| 121 | *cur_output++ = (exp10_f32(tmp) - exp10_f32(-tmp)) / (exp10_f32(tmp) + exp10_f32(-tmp)); |
| 122 | } |
| 123 | } |
| 124 | struct TanhOps : public MTNodeOps |
| 125 | { |
| 126 | TanhOps() |
no test coverage detected